1. 基础
Java 反射机制
Java 动态代理
2. spring IOC(DI)
2.1 ioc 容器
创建
pom.xml 引入 jar 包
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
使用 bean.xml 实现
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.2.xsd">
<context:component-scan base-package="com.cyt"></context:component-scan>
<bean> </bean>
<aop config> </aop> 等
使用 annotation 实现
@Configuration // 说明当前是配置类,作用等同于 2.1 ioc 容器 读取注解类
@ComponentScan("com.cyt") // 指定 spring 在创建容器时要扫描的包
@Import({JdbcConfig.class,TransactionConfig.class}) // 用于在主配置类中导入其他子配置类,实现效果和 @ComponentScan({"com.cyt","config"}) 一样
@PropertySource("classpath:jdbcConfig.properties") //用于指定 properties 文件的位置
@EnableTransactionManagement// 开启对事务管理的支持
public class SpringConfiguration {
@Bean(name = "")
@Scope("prototype")
public Object createBean(){
return new Object();
}
获取
- 读取 bean.xml
- ApplicationContext ac = new ClassPathXmlApplicationContext(“bean.xml”);
- @ContextConfiguration(locations = “classpath:bean.xml”)
- 读取注解类 SpringConfiguration.class
- 读取 bean.xml
创建
- xml 实现
- 使用默认构造函数创建:如果没有无参的构造函数,则无法创建对象
- xml 实现
- 使用普通工厂中的方法创建对象(使用某个类中的方法创建对象,并存入 spring 容器)
- 使用工厂中的静态方法创建对象(使用某个类中的静态方法创建对象,并存入 spring 容器)
- annotation 实现
- Component(value=”bean_id”):默认value是当前类名,其首字母改小写;
- Controller:一般用于表现层, Service:一般用于业务层, Repository:一般用于持久层;
- 补充:这三个注解与 component 实现功能一致,只不过是 spring 框架对应三层使用的一个标志区分;
- 获取
- xml
- IAccountService as = ac.getBean(“accountService3”, AccountServiceImpl3.class);
- annotation
2.3 bean 的参数配置
xml 实现
- bean 标签的 scope 属性: singleton| prototype,request,session,global-session
request: 作用于 web 应用的请求范围;
session: 作用于 web 应用的会话范围;
global-session: 作用于集群环境的会话范围(全局会话范围),当不是集群环境时,它就是 session;- bean 对象的生命周期【spring 框架会依据 scope 属性对单例/多例对象的初始化自动地做不同处理】
- 单例对象【与容器的生命周期相同】:容器创建时对象出生,容器在对象就在,容器销毁时对象销亡;
- 多例对象:使用对象时由 spring 框架创建,只要在使用对象就一直活着,若对象长时间不用且无别的对象引用时,由 java 的垃圾回收器回收;
- 示例
annotation 实现
作用范围:默认使用单例模式-singleton
[@Scope(value="prototype"](#) or "singleton") Class Account{ }
声明周期
- 初始化
@PostConstruct
public void init() { System.out.println(“初始化方法执行啦”); }
- 销毁
@PreDestroy
public void destroy() { System.out.println(“销毁方法执行啦”); }
2.4 依赖注入
2.4.1 bean 类数据
- xml 实现
- 使用 构造函数 注入(使用时必须传入参数,不常用):
<bean id = "accountService" class = "com.cyt.service.impl.AccountServiceImpl">
<constructor-arg name="name" value="test"></constructor-arg>
<constructor-arg name="age" value="18"></constructor-arg>
<constructor-arg name="birthday" ref="now"></constructor-arg>
</bean>
<bean id="now" class="java.util.Date"></bean>
set 方法注入:
<bean id = "accountService2" class = "com.cyt.service.impl.AccountServiceImpl2"> <property name="name" value="cyt"></property> <property name="age" value="25"></property> <property name="birthday" ref="now"></property> </bean>
注入方式3:
- annotation 实现
- Autowired:自动按照类型注入,若存在多个类型匹配,叠用 Qualifer 依据 id 确定 bean 对象;
- Resource:直接按照 bean id 注入,可独立使用;
2.4.2 其他数据
xml 实现
xml 实现: default-autowire - 支持 byName、byType、constructor、no 四种选择;
注:前提是需要在 person 类中体现与 car 类的依赖关系; ```java //1. 全局自动配置 <?xml version=”1.0” encoding=”UTF-8”?>
//2. 局部自动配置(每一个
<!-- 在xml中只定义bean,无需配置依赖关系 -->
<bean id="person" class="com.bravo.xml.Person" autowire="byName"></bean>
<bean id="car" class="com.bravo.xml.Car"></bean>
</bean
- **annotation 实现**
```java
@Component
public class Person {
@Autowired
private Car car;
}
3. spring AOP
3.1 理论
通知类型
pom.xml 引入 jar 包
<dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.7</version> </dependency>
xml
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd> <!--配置Logger类--> <bean id="logger" class="com.cyt.utils.Logger"></bean> <!--配置AOP--> <aop:config> <!--切入点表达式,此处是所有切面都可用--> <aop:pointcut id="pt1" expression="execution(* com.cyt.service.impl.*.*(..))"/> <!--配置切面, ref 说明是对哪个 bean 进行方法加强--> <aop:aspect id="logAdvice" ref="logger"> <!--配置通知的类型,并且建立通知方法和切入点方法的关联--> <aop:before method="beforePrintLog" pointcut-ref="pt1"></aop:before> <aop:after-returningmethod="afterReturningPrintLog"pointcut-ref="pt1"></aop:after-returing> <aop:after-throwingmethod="afterThrowingPrintLog"pointcut-ref="pt1"></aop:after-throwing> <aop:after method="afterPrintLog" pointcut-ref="pt1"></aop:after> <aop:around method="aroundPrintLog" pointcut-ref="pt1"></aop:around> </aop:aspect> </aop:config>
annotation(用于需要加强的类上)
- 类注解 :
- @Component()
- @Aspect(“logAdvice”)
- @EnableAspectJAutoProxy
- 方法注解
- @Pointcut(“execution(“”) public void pt1(){}
- @Before(“pt1()”) 、@AfterReturning(“pt1()”)、@AfterThrowing(“pt1()”)、@After(“pt1()”)、@Around(“pt1()”)
- 案例:参照 5.3.2 AOP 包装;
- 类注解 :
- 注意:执行时,最终通知和后置通知执行顺序有变,在实际开发中应慎重,建议采用环绕通知,采用环绕通知后就无需显示调用其他通知,而是在环绕函数中调用;
补充
- 切入点表达式
- 内容:访问修饰符 返回值 包名.包名.包名…类名.方法名(参数列表)
- 标准表达式: eg. public void com.cyt.service.impl.AccountServiceImpl21_aop.saveAccount()
- 简化表达式
- 访问修饰符可省略: void com.cyt.service.impl.AccountServiceImpl21_aop.saveAccount() - 返回值可使用通配符,表示任意返回值 - * com.cyt.service.impl.AccountServiceImpl21_aop.saveAccount() - 包名可以使用通配符,表示任意包,但是有几级包,就需要写几个*. - eg. * *.*.*.*.AccountServiceImpl21_aop.saveAccount() - 包名可以使用..表示当前包及其子包: - eg. * *..AccountServiceImpl21_aop.saveAccount() - 类名和方法名都可以使用 * 来实现通配: - eg. * *..*.*() 【针对无参方法】 - 可以直接写数据类型: - 基本类型直接写名称:* *..*.*(int) - 引用类型写包名.类名的方式:全通配写法:* *..*.*(..)
- 实际开发中切入点表达式的通常写法:
- 切到业务层实现类下的所有方法 com.cyt.service.impl..*(..)
- 实际开发中切入点表达式的通常写法:
- 此标签写在 aop:aspect 标签内部就只能被当前切面使用,其还可以写在 aop:aspect 外面,此时就变成了所有切面可用;
4. spring 与 Junit 集成
4.1 Junit 特性
- 切入点表达式
junit 本身集成有一个 main 方法,该方法会自动判断当前测试类中哪些方法有 @Test 注解,然后让有 Test 注解的方法执行;
- 独立于 spring 框架,在执行测试方法时,junit 根本不知道是否使用 spring 框架,因此并不会自动读取配置文件 或 类创建的 spring 核心容器;
测试方法执行时无 ioc 容器,即便写了 Autowired 注解也无法实现注入;
4.2 集成配置
注意:**当使用 spring 5.x 版本的时候,要求 junit 的 jar 必须是 4.12 及以上;**
导入 spring 整合 junit 的 jar 包(坐标)
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
- 使用 junit 提供的一个注解把原有的 main 方法替换,替换成 spring 提供的 @Runwith
- @RunWith(SpringJUnit4ClassRunner.class)
告知 spring 的运行期,spring 和 ioc 创建是基于 xml 还是注解类,并说明位置 @ContextConfiguration,详见2.1 ioc 容器获取
5. spring 事务管理
5.1 配置
第一步:引入 jar 包
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>5.0.2.RELEASE</version> </dependency>
第二步: bean.xml 引入
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
第三步:bean.xml 配置事务管理器
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <!--配置数据源--> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://localhost:3306/eesy"></property> <property name="username" value="root"></property> <property name="password" value="1234"></property> </bean>
5.2 使用
xml
第一步:在 bean.xml 中开启 spring 对事务的支持:
<tx:annotation-driven transaction-manager="transactionManager"> </tx:annotation-driven>
第二步:在 bean.xml 配置事务的通知
<tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="*" propagation="REQUIRED" read-only="false"/> <tx:method name="find" propagation="SUPPORTS" read-only="true"></tx:method> </tx:attributes> </tx:advice>
第三步:在 bean.xml 配置 AOP
<aop:config> <aop:pointcut id="pt1" expression="execution(* com.cyt.service.impl.*.*(..))"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="pt1"></aop:advisor> </aop:config>
annotation
- 用于类
@EnableTransactionManagement// 开启对事务管理的支持
@Transactional(propagation = Propagation.REQUIRED, readOnly = false)
- 用于类或方法上
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true
5.3 自定义一个简单的事务管理类
5.3.1 BeanProxy(xml版)
- bean.xml
public class BeanFactory {
private IAccountService1 accountService1;
private TransactionManager tsManager;
public void setAccountService1(IAccountService1 accountService1) {
this.accountService1 = accountService1;
}
public void setTsManager(TransactionManager tsManager) {
this.tsManager = tsManager;
}
public IAccountService1 getAccountService1() {
// 织入:把增强应用到目标对象来创建新的代理对象的过程
IAccountService1 asProxy = (IAccountService1) Proxy.newProxyInstance(
accountService1.getClass().getClassLoader(),
accountService1.getClass().getInterfaces(),
//切面(Aspect)是切入点和通知(引介)的结合
new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object returnValue = null;
try {
//1. 开启事务
tsManager.beginConnection();//前置通知
//2. 执行操作
returnValue = method.invoke(accountService1, args); // 整个invoke方法在执行就是环绕通知,且环绕通知中有明确的切入点方法调用
//3. 提交事务
tsManager.commit();//后置通知
//4. 返回结果
return returnValue;
} catch (Exception e) {
//5. 回滚操作
tsManager.rollback(); //异常通知
throw new RuntimeException(e);
} finally {
//6. 释放资源
tsManager.release(); //最终通知
}
}
});
return asProxy;
}
}
5.3.2 AOP 包装(注解版)
- 分析:
- proxy.invoke() 方法 和 AOP 实现中 环绕通知 的实现逻辑是一致的;
- 为实现通用性:
- 切入点表达式:说明要做加强处理的包、类或方法;
- 在环绕通知内提供了一个 ProceedingJoinPoint 类来做具体执行;
@Component("txManager") @Aspect @EnableAspectJAutoProxy public class TransactionManager { @Autowired private ConnectionUtils connectionUtils; @Pointcut("execution(*com.cyt.service.impl.AccountServiceImpl11.transfer(..))") private void pointCut(){} @Before("pointCut()") public void beginConnection(){ try { connectionUtils.getThreadConnection().setAutoCommit(false); } catch (Exception e) { e.printStackTrace(); } } @AfterReturning("pointCut()") public void commit(){ try { connectionUtils.getThreadConnection().commit(); } catch (Exception e) { e.printStackTrace(); } } @AfterThrowing("pointCut()") public void rollback(){ try { connectionUtils.getThreadConnection().rollback(); } catch (Exception e) { e.printStackTrace(); } } @After("pointCut()") public void release(){ try { connectionUtils.getThreadConnection().close(); connectionUtils.removeConnection(); } catch (Exception e) { e.printStackTrace(); } } @Around("pointCut()") public void aroundTansfer(ProceedingJoinPoint pjp){ Object[] args = pjp.getArgs(); try { //1. 开启事务 beginConnection(); //2. 执行操作 pjp.proceed(args); //3. 提交事务 commit(); //4. 返回结果 } catch (Throwable t) { //5. 回滚操作 rollback(); throw new RuntimeException(t); } finally { //6. 释放资源 release(); } } }
6. spring 连接数据库
6.1 Dbutil
pom.xml 引入 jar 包
<dependency> <groupId>commons-dbutils</groupId> <artifactId>commons-dbutils</artifactId> <version>1.4</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.6</version> <scope>runtime</scope> </dependency> <dependency> <groupId>c3p0</groupId> <artifactId>c3p0</artifactId> <version>0.9.1.2</version> </dependency>
配置
方式一:xml
<bean id="accountDao" class="com.cyt.dao.impl.AccountDaoImpl"> <property name="runner" ref="runner"></property> <property name="connectionUtils" ref="connectionUtils"></property>//保证单一连接 </bean> <bean id="runner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype"> <constructor-arg name="ds" ref="dataSource"></constructor-arg> </bean> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="com.mysql.jdbc.Driver"></property> <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/eesy"></property> <property name="user" value="root"></property> <property name="password" value="1234"></property> </bean>
方式二:annotation ```java @Configuration @PropertySource(“classpath:jdbcConfig.properties”) public class JdbcConfig { @Value(“${jdbc.driver}”) private String driver; @Value(“${jdbc.url}”) private String url; @Value(“${jdbc.username}”) private String user; @Value(“${jdbc.password}”) private String password;
@Bean(name = “runner”) @Scope(“prototype”) public QueryRunner createQueryRunner(DataSource dataSource){ return new QueryRunner(dataSource); } @Bean(name = “dataSource”) public DataSource createDataSource(){ try {
ComboPooledDataSource ds = new ComboPooledDataSource(); ds.setDriverClass(driver); ds.setJdbcUrl(url); ds.setUser(user); ds.setPassword(password); return ds;
} catch (Exception e) {
throw new RuntimeException(e);
} } } ```
CURD 操作
select
List<Account1> account1s = runner.query( connectionUtils.getThreadConnection(), "select * from account where name = ?", new BeanListHandler<Account1>(Account1.class), accountName); Account account = runner.query( connectionUtils.getThreadConnection(), "select * from account", new BeanListHandler<Account1>(Account1.class));
insert
runner.update(connectionUtils.getThreadConnection(), "insert into account(name,money) values(?,?)", account1.getName(),account1.getMoney());
update
runner.update(connectionUtils.getThreadConnection(), "update account set name=?,money=? where id=?", account1.getName(),account1.getMoney(),account1.getId());
delete
runner.update(connectionUtils.getThreadConnection(), "delete from account where id=?", accountId);
6.2 JdbcTemplate
pom.xml 引入 jar 包
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.0.2.RELEASE</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.6</version> <scope>runtime</scope> </dependency>
配置
方式一:xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- 读取db.properties配置文件到Spring容器中 <context:property-placeholder location="classpath:db.properties" /> --> <!--配置 AccountDao12--> <bean id="accountDao12" class="com.cyt.dao.impl.AccountDaoImpl12"> <!-- <property name="jt" ref="jdbcTemplate"></property>--> <property name="dataSource" ref="dataSource"></property> </bean> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"></property> </bean> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://localhost:3306/eesy"></property> <property name="username" value="root"></property> <property name="password" value="1234"></property> </bean> </beans>
方式二:annotation ```java @Configuration @PropertySource(“classpath:jdbcConfig.properties”) public class JdbcConfig { @Value(“${jdbc.driver}”) private String driver; @Value(“${jdbc.url}”) private String url; @Value(“${jdbc.username}”) private String user; @Value(“${jdbc.password}”) private String password;
@Bean(name=”jdbcTemplate”) public JdbcTemplate createJdbcTemplate(DataSource dataSource){ return new JdbcTemplate(dataSource); } @Bean(name=”dataSource_DM”) public DataSource createDataSource_DM(){ DriverManagerDataSource ds = new DriverManagerDataSource(); ds.setDriverClass(driver); ds.setJdbcUrl(url); ds.setUser(user); ds.setPassword(password); return ds; } } ```
- 方式三: 继承 JdbcDaoSupport,此时不需要配置 和 注入 jdbcTempalte 的 Bean;
CURD 操作 ```java public class AccountDaoImpl12 extends JdbcDaoSupport implements IAccountDao1 {
public Account1 findAccountById(Integer accountId) { List
account1s = getJdbcTemplate().query( "select * from account where id = ?", new BeanPropertyRowMapper<Account1>(Account1.class), accountId);
return account1s.isEmpty()?null:account1s.get(0); } public void updateAccount(Account1 account1) { getJdbcTemplate().update(
"update account set name=?,money=? where id=?", account1.getName(),account1.getMoney(),account1.getId());
} public void saveAccount(Account1 account1) { getJdbcTemplate().update(
"insert into account(name,money) values(?,?)", account1.getName(),account1.getMoney());
} public void deleteAccount(Integer accountId) { getJdbcTemplate().update(
"delete from account where id=?", accountId);
} }
```
6.3 Mybatis
7. Spring 的加载机制
参考