1. Spring
Spring是分层的JavaSE/EE应用 full-stack轻量级 一站式 开源框架
体系结构:
2. IoC 反转
IoC(Inversion Of Control) 控制反转,Spring反向控制应用程序所需要使用的外部资源
Spring控制的资源全部放置在Spring容器中,该容器称为IoC容器
2.1. 耦合与内聚
耦合(Coupling): 代码书写过程中所使用技术的结合紧密度,用于衡量软件中各个模块之间的互联程度
内聚(Cohesion):代码书写过程中单个模块内部各组成部分间的联系,用于衡量软件中各个功能模块内部的功能联系
我们追求 高内聚 低耦合
2.2. 工厂模式
2.3. 创建项目
创建maven项目
导入模块
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
</dependencies>
在resources中创建applicationContext.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">
<!-- bean为映射标签 创建spring控制的资源 id为操作空间 class为实现类引用路径-->
<bean id="userService" class="com.itheima.service.impl.UserServiceImpl"/>
</beans>
使用方法
//2.加载配置文件
ApplicationContext ctx =new ClassPathXmlApplicationContext("applicationContext.xml");
//3.获取资源
UserService userService = (UserService) ctx.getBean("userService");
userService.save();
2.4. bean
<bean>
标签 定义spring中的资源, 此标签定义的资源将受到spring控制id属性 bean的名词 通过id值获取到bean
class属性 bean的类型
name属性 bean的别名 我们可以通过id或者name来获取到bean 并且name可以起多个别名 别名之间用逗号隔开
scope属性 定义bean的作用范围
- singleton值 设置创建出的对象保存在spring容器中 是一个单例对象 默认值 单例时创建spring容器时就加载bean绑定的类
- prototype值 设置创建出的对象保存在spring容器中 是一个非单例对象 非单例则是在getBean时创建类
- request、session、application、websocket值 设置创建出的对象放置在web容器对应的位置
init-method bean对象初始化时执行指定方法
- 值为bean对应的类中的具体方法名
destroy-method bean对象销毁时执行指定方法
- 值为bean对应的类中的具体方法名 只有ClassPathXmlApplicationContext才有close方法 spring默认自动销毁但销毁方法不会执行
factory-method属性
- 值为静态工厂中创建对象的方法
factory-bean属性
值为实例工厂的id 需要先用一个bean绑定实例工厂类 然后第二个bean的factory-bean为第一个bean的id 然后用factory-method调用其实例工厂的方法
<!-- factory-bea 实例工厂创建对象 需要bean先绑定实例工厂类 然后第二个bean的factory-bea为实例工厂的id 然后factory-method调用其创建对象方法-->
<bean id="userService3" class="com.itheima.service.UserServiceFactory2" />
<bean factory-bean="userService3" factory-method="getService"/>
3. DI 依赖注入
DI(Dependency Injection) 依赖注入,应用程序运行依赖的资源由Spring为其提供,资源进入应用程序的方式称为注入
3.1. set注入
<property>
标签 使用set方法的形式为bean提供资源 是bean的子标签name属性
- 值为对应bean中的属性名 要该属性必须为私有属性同时提供set方法
value属性
- 值为name绑定属性的 值 设定非引用类型属性的值
ref属性
- 值为引用类型属性对象bean的id 不能与value同时使用
配置
<bean id="userService" class="com.itheima.service.impl.UserServiceImpl">
<!-- 将要注入的引用类型变量通过property属性进行注入 对应的name是要注入的变量名 使用ref声明要注入的bean的id -->
<property name="userDao" ref="userDao"></property>
<!-- 如果要注入的变量为一个值 我们通过value来设置 同样需要在类中设置私有变量 和set方法-->
<property name="num" value="13"/>
</bean>
<!-- 将要注入的资源声明为bean-->
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"/>
反转资源注入
import com.itheima.dao.UserDao;
import com.itheima.service.UserService;
public class UserServiceImpl implements UserService {
private UserDao userDao;
private int num;
//对需要进行注入的变量添加set方法
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void setNum(int num) {
this.num = num;
}
@Override
public void save() {
userDao.save();
System.out.println("running...");
System.out.println(num);
}
}
调用
public class UserApp {
public static void main(String[] args) {
//2.加载配置文件
ApplicationContext ctx =new ClassPathXmlApplicationContext("applicationContext.xml");
//3.获取资源
UserService userService = (UserService) ctx.getBean("userService");
userService.save();
}
}
3.2. 构造器注入
<constructor-arg>
标签 使用构造方法的形式为bean提供资源 是bean的子标签name属性
- 值为对应bean中的属性名 要该属性必须为私有属性同时提供set方法
value属性
- 值为name绑定属性的 值 设定非引用类型属性的值
ref属性
- 值为引用类型属性对象bean的id 不能与value同时使用
type属性
- 值为设定构造方法参数的类型 用于赋值给指定的变量类型 推荐使用nanme指定变量
index属性
- 值为设定构造方法参数的位置 用于赋值给指定 构造方法中 变量的index 从0开始 推荐使用name指定变量
被注入类要声明构造方法并赋值
private UserDao userDao;
private int num;
public UserServiceImpl(UserDao userDao,int num){
this.userDao=userDao;
this.num=num;
}
bean配置
<!-- 构造方法注入-->
<!-- 注入类-->
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"/>
<!--被注入的类-->
<bean id="userService" class="com.itheima.service.impl.UserServiceImpl">
<constructor-arg ref="userDao" />
<constructor-arg name="num" value="456"/>
</bean>
3.3. 集合类型数据注入
如<array>、<list>、<set>
、<map>
、<props>
等集合 是归属于property或constructor-arg标签的子标签
<list>
<value>
标签- 值为元素对于的值
<props>
<prop>
标签key属性
- 值为属性名
- 值为元素对于的值
<array>
<value>
标签- 值为元素对于的值
<set>
<value>
标签- 值为元素对于的值
<enrty>
标签key属性
- 值为key
value属性
- 值为值
<!-- 集合注入-->
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"/>
<bean id="userService" class="com.itheima.service.impl.UserServiceImpl">
<property name="userDao" ref="userDao"/>
<property name="bookDao" ref="bookDao"/>
</bean>
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
<property name="al">
<list>
<value>helll</value>
<value>world</value>
</list>
</property>
<property name="properties">
<props>
<prop key="name">age</prop>
<prop key="value">19</prop>
</props>
</property>
<property name="arr">
<array>
<value>123</value>
<value>456</value>
</array>
</property>
<property name="hs">
<set>
<value>111</value>
<value>222</value>
</set>
</property>
<property name="hm">
<map>
<entry key="name" value="zhangsan"/>
<entry key="name" value="lisi"/>
</map>
</property>
</bean>
3.4. p命名空间 简化配置
增加属性 xmlns:p=”http://www.springframework.org/schema/p“ 配置p命名空间
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
p命名空间为bean注入属性值 替代property
<!-- 两个bean一样的-->
<bean id="userService" class="com.itheima.service.impl.UserServiceImpl" p:bookDao-ref="bookDao" p:userDao-ref="userDao" />
<bean id="userService" class="com.itheima.service.impl.UserServiceImpl">
<property name="userDao" ref="userDao"/>
<property name="bookDao" ref="bookDao"/>
</bean>
3.5. SpEL EL表达式
spring通过EL表达式的支持,统一属性注入格式
在value中书写el表达式
properties文件
增加属性 xmlns:context=”http://www.springframework.org/schema/context“
并加上约束 http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
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
https://www.springframework.org/schema/context/spring-context.xsd
">
加context命名空间的支持
<!-- context命名空间 加载类路径下 所有的properties文件 加载后使用${属性名} -->
<context:property-placeholder location="classpath:*.properties"/>
<bean id="userService" class="com.itheima.service.impl.UserServiceImpl">
<constructor-arg ref="userDao" />
<constructor-arg name="num" value="${age}"/>
</bean>-->
3.6. import 团队开发
xml中通过<import>
标签我们可以引入外部的IoC配置
<impoprt>
标签 引用外部IoC配置- resource属性 配置文件名
3.7. 容器运行时加载多个IoC配置
ApplicationContext ctx =new ClassPathXmlApplicationContext("applicationContext.xml","applicationContext2.xml");
3.8. bean注意事项
- id是唯一的 同一文件中不允许存在相同id 而不同文件中后定义覆盖前面的
- 导入配置文件可以理解为 将配置文件复制粘贴到对应位置
- 导入配置文件顺序不同可能导致程序运行结果不同
3.9. ApplicationContext对象
3.10. 第三方资源bean配置
添加druid模块 druid是阿里巴巴开发的jdbc连接池组件
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.21</version>
</dependency>
配置文件
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/heima_mm"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
创建
ApplicationContext ctx =new ClassPathXmlApplicationContext("applicationContext.xml");
DruidDataSource dataSource = (DruidDataSource) ctx.getBean("dataSource");
System.out.println(dataSource);
看清楚 第三方类的类名 属性名 set方法
3.11. Spring+MyBatis
applicationContext.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
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
https://www.springframework.org/schema/context/spring-context.xsd
">
<context:property-placeholder location="classpath:*.properties"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!-- spring整合mybatis后控制创建连接用的对象-->
<bean class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="typeAliasesPackage" value="com.itheima.domain"/>
</bean>
<!-- 加载mybatis映射配置扫描 将其作为spring的bean进行管理-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.itheima.dao"/>
</bean>
</beans>
pox.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>spring+mybatis</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.3</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.21</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.3</version>
</dependency>
</dependencies>
</project>
4. 注解
注解代替xml配置可以简化配置 提供开发效率
https://www.cnblogs.com/alter888/p/9083963.html
并且要在applicationContext.xml中配置组件扫描
<!-- 配置组件扫描-->
<context:component-scan base-package="com.itheima"/>
5. 新注解
使用上面的注解还不能完全替代xml配置 如第三方的类
@Bean 标记第三方类
public class JDBCConfig {
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String userName;
@Value("${jdbc.password}")
private String password;
@Bean("dataSource")
public DataSource getDataSource(){
DruidDataSource ds = new DruidDataSource();
ds.setDriverClassName(driver);
ds.setUrl(url);
ds.setUsername(userName);
ds.setPassword(password);
return ds;
}
}
6. 加载控制
@DependsOn(“classname”)
@Order(n) 控制加载顺序
@Lazy 延迟加载
7. 整合Junit
在Spring中之前我们测试都需要获取容器 然后获取bean
导入坐标
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
测试样例
//设定spring专用的类加载器
@RunWith(SpringJUnit4ClassRunner.class)
//设定加载的spring上下文对应的配置
@ContextConfiguration(classes = SpringConfig.class)
public class UserServiceTest {
@Autowired
private AccountService accountService;
@Test
public void testFindById(){
Account ac = accountService.findById(2);
// System.out.println(ac);
//assert 预计值 结果值 如果不一致则测试不通过
Assert.assertEquals("Jock",ac.getName());
}
@Test
public void testFindAll(){
List<Account> list = accountService.findAll();
Assert.assertEquals(2,list.size());
}
}
8. Ioc底层核心原理
9. 组件扫描过滤器
@ComponentScan 组件扫描器拥有过滤指定组件功能
按注解类型 过滤
- excludeFilters 设置排除性过滤器
- includeFilters 设置包含性过滤器
//所有的@Service注解被过滤
@ComponentScan(value = "com.itheima",excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = Service.class))
9.1. 自定义组件过滤器
继承TypeFilter 实现match方法 返回false则不过滤 返回true则过滤
package com.itheima.config.filter;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.TypeFilter;
import java.io.IOException;
public class MyTypeFilter implements TypeFilter {
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
ClassMetadata classMetadata = metadataReader.getClassMetadata(); //获取class的元数据
String className = classMetadata.getClassName(); //获取类名
System.out.println(className);
if(className.equals("com.itheima.service.impl.AccountServiceImpl")){
return true;
}
return false;
}
}
- 按自定义过滤器 过滤
@ComponentScan(value = "com.itheima",excludeFilters = @ComponentScan.Filter(type = FilterType.CUSTOM,classes = MyTypeFilter.class))
10. 自定义导入器
继承 ImportSelector 实现selectImports方法
package com.itheima.config.selector;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;
import java.util.ResourceBundle;
public class MyImportSelector implements ImportSelector {
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
//使用properties文件读取
ResourceBundle bundle = ResourceBundle.getBundle("import"); //文件名
String className = bundle.getString("className"); //属性名
return new String[]{className};
// return new String[]{"com.itheima.service.impl.AccountServiceImpl"}; //直接返回指定类不推荐使用
}
}
导入指定的组件 不用需要bean绑定或者注解绑定
@Import(MyImportSelector.class)
11. 自定义注册器
继承 ImportBeanDefinitionRegistrar 实现 registerBeanDefinitions 方法
package com.itheima.config.registrar;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.TypeFilter;
import java.io.IOException;
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry,false);
scanner.addIncludeFilter(new TypeFilter() {
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
return true;
}
});
scanner.scan("com.itheima");
}
}
@Import(MyImportBeanDefinitionRegistrar.class) //自定义注册器
12. bean初始化过程
13. AOP
Aspect Oriented Programming 面向切门编程 通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术
AOP 是 OOP(面向对象) 的延续
作用:在程序运行期间 在不修改源码的情况下对方法进行功能增强
优势:减少重复代码 提高开发效率 并且便于维护
13.1. AOP的动态代理对象
- JDK代理 : 基于接口的动态代理技术
- cglib代理 : 基于父类的动态代理技术
导入坐标
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
把共性的功能提前出来 并提供类和方法
13.2. XML配置
添加aop 以下声明
xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
标签头
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
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/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
绑定
<!--3.开启AOP命名空间-->
<bean id="userService" class="com.itheima.service.impl.UserServiceImpl"/>
<!--2.配置共性功能成功spring控制的资源 共性功能的类-->
<bean id="myAdvice" class="com.itheima.aop.AOPAdvice"/>
<!--4.配置AOP-->
<aop:config>
<!--5.配置切入点-->
<aop:pointcut id="pt" expression="execution(* *..*(..))"/>
<!--6.配置切面(切入点与通知的关系)-->
<aop:aspect ref="myAdvice">
<!--7.配置具体的切入点对应通知中那个操作方法 pointcut-ref为pointcut对应的id-->
<aop:before method="function" pointcut-ref="pt"/>
</aop:aspect>
</aop:config>
<aop:config>
标签 aop根标签 在beans可以拥有多个<aop:aspect>
标签 可以在aop:config中配置多个- ref属性 通知(共性类)所在的bean的id
<aop:before>
标签 切面- method属性 通知中具体的方法
- pointcut-ref属性 与aop:pointcut中的id要一致
- pointcut=”execution( ..*(..))” 私有切入点 不能与pointcut-ref共存
<aop:pointcut>
标签 也可以拥有多个 上级未config则为公共切入点 上级未aspect则为局部切入点- id属性 名称 可以自定义
- expression属性 切入点模式
13.2.1. 切入点表达式
支持逻辑运算符 和非运算
13.2.2. 通知类型
<aop:before>
前置通知 如果通知中抛出异常 则阻止原始方法运行<aop:after>
后置通知 无论是否异常 都会执行通知<aop:after-running>
运行通知 如果抛出异常无法通知<aop:after-throwing>
异常通知 如果没有抛出异常无法通知<aop:around>
环绕通知 在原始方法执行前后都有对应执行的执行,还可以阻止原始方法执行
13.2.3. 通知顺序
当同一个切入点配置多个通知时,标签配置的顺序就是执行的顺序
13.2.4. 通知获取数据
获取参数 所有的通知都可以获取参数
public void before(JoinPoint jp) throws Throwable{
Object[] args = jp.getArgs();
}
第二个方法
在通知方法中定义变量
public void before2(int a) {
System.out.println(a);
}
-
在applicationContext.xml 配置aop传参
-
<!-- &是& args(a) 为aop通知方法中形参的名字 -->
<aop:before method="before2" pointcut="execution(* *..*(..)) && args(a)"/>
13.2.5. 通知获取返回值
获取返回值 只有around 和 after-returning 通知
public void before2(Object ret) {
System.out.println(ret);
}
<!-- returning为通知方法形参的名字 此变量为原始方法的返回值 -->
<aop:after-returning method="before2" pointcut-ref="pt" returning="ret"/>
第二种方法
public Object around (ProceedingJoinPoint pjp) throws Throwable {
//对原始方法的调用 返回原始方法的返回值
Object proceed = pjp.proceed();
System.out.println(proceed);
//必须返回返回值 不然原始方法会异常
return proceed;
}
-
<aop:around method="around" pointcut-ref="pt"/>
13.2.6. 获取异常
获取异常 around 和 after-throwing 通知获取
public void afterThrowing(Throwable t){
System.out.println(t.getMessage());
}
<!-- throwing为通知方法中形参的名称 用于获取异常 -->
<aop:after-throwing method="afterThrowing" pointcut-ref="pt" throwing="t"/>
第二种方法
public Object around (ProceedingJoinPoint pjp) {
//对原始方法的调用 返回原始方法的返回值
Object proceed = null;
try {
proceed = pjp.proceed();
} catch (Throwable e) {
e.printStackTrace();
}
System.out.println(proceed);
//必须返回返回值 不然原始方法会异常
return proceed;
}
-
<aop:around method="around" pointcut-ref="pt"/>
4. 注解
开aop注解支持
<aop:aspectj-autoproxy/>
在AOP类上加注解
```java @Component //bean绑定 @Aspect //标记AOP 注解 public class AOPAdvice { @Pointcut(“execution( ..*(..))”) //定义一个空方法 并且绑定为aop id=pt public void pt(){}
@Before(“pt()”) //标记为aop-before方法 并且绑定空间名称 pt() public void before(JoinPoint jp) throws Throwable { Object[] args = jp.getArgs(); }
@After(“pt()”) public void function() { System.out.println(“共性功能”); }
@Around(“pt()”) public Object around (ProceedingJoinPoint pjp) { //对原始方法的调用 返回原始方法的返回值 Object proceed = null; try {
proceed = pjp.proceed();
} catch (Throwable e) {
e.printStackTrace();
} System.out.println(proceed); //必须返回返回值 不然原始方法会异常 return proceed; }
@AfterReturning(value = “pt()”, returning = “ret”) public void before2(Object ret) { System.out.println(ret); }
@AfterThrowing(value = “pt()”, throwing = “t”) public void afterThrowing(Throwable t) { System.out.println(t.getMessage()); }
}
-
[@Aspect ](/Aspect ) 标记为aop 在AOP类上 记得实例化并[@Component ](/Component )
-
[@Pointcut ](/Pointcut ) 定义个一个空参无返回值的空方法 为此aop绑定空间名称 为方法名 如果在其他类中创建 则通知中调用要加上类名 如: aopconfig.pt()
-
[@After ](/After ) 后置通知
-
[@Before ](/Before ) 前置通知
<a name="4e0e0ce7"></a>
### 13.3.1. 注解通知执行顺序
1. 与方法定义位置无关
2. 如果通知类型同 与方法名自然排序来执行
3. 如果是不同AOP的通知 是与AOP类名有关
4. 在类上方 使用注解 @Order(n) 自定义顺序
<a name="9e28a5ff"></a>
### 13.3.2. AOP配置 (注解)
前面我们用xml配置注解
- @EnableAspectJAutoProxy 开启aop注解 在spring配置类中标记注解
<a name="73b3dce7"></a>
## 13.4. 静态代理
装饰者模式 (Decorator Pattern) 在不惊动原始设计的基础上,为其添加功能
创建一个新的类 并继承原接口 有参构造方法 形参为原始对象 调用原始方法
```java
public class UseServiceImplDecorator implements UserService {
private UserService userService;
public UseServiceImplDecorator(UserService userService) {
this.userService = userService;
}
@Override
public void save() {
userService.save();
System.out.println("刮大白");
}
}
13.5. JDK动态代理
public class UserServiceJDKProxy {
public static UserService createUserServiceJDKProxy(UserService userService) {
ClassLoader cl = userService.getClass().getClassLoader(); //获取加载类
Class[] classes = userService.getClass().getInterfaces(); //获取类接口
InvocationHandler ih = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object invoke = method.invoke(userService, args); //调用原始方法
System.out.println("刮大白"); //不影响原始类下实现增强功能
return invoke;
}
};
UserService service = (UserService) Proxy.newProxyInstance(cl, classes, ih);
return service;
}
}
13.6. CGLIB
CGLIB(Code Generation Library) Code生成类库 不限定是否有具体接口 无需原始代理对象
public class UserServiceCglibProxy {
public static UserService createUserServiceCglibProxy(Class claszz) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(claszz); //设置enhancer的父类
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
// method.invoke(o,objects);
// methodProxy.invoke(o,objects);
System.out.println(method.getName()); //获取方法名
Object ret = methodProxy.invokeSuper(o, objects);//原始方法调用 默认对所有方法做增强
if (method.getName().equals("save")) { //需要判断方法名
System.out.println("刮大白");
}
return ret;
}
});
return (UserService) enhancer.create();
}
}
测试类
public static void main(String[] args) {
UserService userService = UserServiceCglibProxy.createUserServiceCglibProxy(UserServiceImpl.class);
userService.save();
}
13.7. AOP底层 切换动态代理方式
AOP底层使用的JDK的动态代理方式
我们可以配置为使用CGLIB方式 需要在aop:config中配置
<!--XML配置AOP 默认为flase 为jdk动态代理 true为CGLIB代理 -->
<aop:config proxy-target-class="true">
<!-- 注解配置AOP-->
<aop:aspectj-autoproxy proxy-target-class="false"/>
//注解驱动
@EnableAspectJAutoProxy(proxyTargetClass = true)
13.8. 织入时机
14. 事务
事务指数据库中多个操作合并在一起形成的操作序列
- 当操作出现失败 回滚事务 保障数据的一致性
- 当并发访问数据库时 防止并发访问操作结果相互干扰
14.1. 事务核心对象
14.2. 编程式事务
private DataSource dataSource;
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
//开启事务
PlatformTransactionManager ptm = new DataSourceTransactionManager(dataSource);
//事务定义
TransactionDefinition td = new DefaultTransactionDefinition();
//事务状态
TransactionStatus ts = ptm.getTransaction(td);
accountDao.inMoney(outName, money);
// int i = 1 / 0;
accountDao.outMoney(inName, money);
//提交事务
ptm.commit(ts);
业务层要注入dataSource
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
<property name="accountDao" ref="accountDao"/>
<property name="dataSource" ref="dataSource"/>
</bean>
14.3. AOP改造编程式事务
private DataSource dataSource;
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
public Object transactionManager(ProceedingJoinPoint pjp) throws Throwable {
//开启事务
PlatformTransactionManager ptm = new DataSourceTransactionManager(dataSource);
//事务定义
TransactionDefinition td = new DefaultTransactionDefinition();
//事务状态
TransactionStatus ts = ptm.getTransaction(td);
Object ret = pjp.proceed(pjp.getArgs());
//提交事务
ptm.commit(ts);
return ret;
}
<bean id="txAdvice" class="com.itheima.aop.TxAdvice">
<property name="dataSource" ref="dataSource"/>
</bean>
<aop:config>
<aop:pointcut id="pt" expression="execution(* *..transfer(..))"/>
<aop:aspect ref="txAdvice">
<aop:around method="transactionManager" pointcut-ref="pt"/>
</aop:aspect>
</aop:config>
14.4. 声明式事务
声明添加域
xmlns:tx=”http://www.springframework.org/schema/tx“
http://www.springframework.org/schema/tx
https://www.springframework.org/schema/tx/spring-tx.xsd
<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"
xmlns:aop="http://www.springframework.org/schema/aop"
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/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
https://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<bean class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="typeAliasesPackage" value="com.itheima.domain"/>
</bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.itheima.dao"/>
</bean>
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
<property name="accountDao" ref="accountDao"/>
</bean>
<!-- tx声明-->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 定义事务管理的通知类-->
<tx:advice id="txAdvice" transaction-manager="txManager">
<!--定义要控制的事务-->
<tx:attributes>
<!-- 指定方法控制事务 read-only 是否开启只读事务-->
<!-- <tx:method name="transfer" read-only="false"/>-->
<tx:method name="*" read-only="false"/>
<tx:method name="get*" read-only="true"/>
<tx:method name="find*" read-only="true"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="pt" expression="execution(* com.itheima.service.*Service.*(..))"/>
<!-- advice可以是普通类不实现接口或没有继承关系 advisor通知类必须实现通知接口 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt"/>
</aop:config>
14.5. 事务传播行为
需要指定传播属性则在tx:method 的 propagation 属性配置
<tx:method name="a父" read-only="false" propagation="REQUIRED"/>
<tx:method name="b子" read-only="false" propagation="REQUIRED"/>
14.6. 注解事务
在业务层 sql操作接口(全部抽象方法 推荐这个 或接口中的抽象方法)/类(全部方法)/方法上都可以 上面加 @Transactional
@Transactional(readOnly = false, timeout = -1, isolation = Isolation.DEFAULT, rollbackFor = {IOException.class},propagation = Propagation.REQUIRED)
public void transfer(String outName, String inName, Double money);
- 开启tx注解驱动 xml版
<!--事务管理为事务注解绑定的方法-->
<tx:annotation-driven transaction-manager="txManager"/>
- 注解版 注解驱动 在springconfig类上标记
@EnableTransactionManagement
并且配置事务核心对象
@Bean
public PlatformTransactionManager getTransactionManager(DataSource dataSource){
return new DataSourceTransactionManager(dataSource);
}