把一个个的横切关注点放到某个模块中去,称之为切面。那每一个的切面都能影响业务的某一种功能,切面的目的就是功能增强,如日志切面就是一个横切关注点,应用中许多方法需要做日志记录的只需要插入日志的切面即可。
AOP术语:
1.Joinpoint连接点,被拦截到需要被增强的方法。where:去哪里做增强
2.Pointcut:切入点,哪些包中的哪些类中的哪些方法,可以认为是连接点的集事。where:去哪些地方做增强
3.Advice:增强,当拦截到Joinpoint之后,在方法执行的时机(when)做什么样(what)的增强,根据时机分为:前置增强,后置增强,异常增强,最络增强,环绕增强
4.Aspect:切面,Pointcut+Advice,去哪些地方+在什么时机+做什么增强
5.Target:目标对象,被代理的目标对象
6.Wearing:织入,把Advice加到Target上之后,创建出Proxy对象的过程
7.Proxy:一个类被AOP织入增强后,产生的代理类
Advice(增强)执行时机:
Pointcut语法:
execution(modifiers-pattern? ret0type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)
中文:
execution(<修饰符>? <返回类型> <声明类型>? <方法名>(<参数>) <异常>?)
切入点表达式中的通配符:
*:匹配任何部分,但是只能表示一个单词
..:可用于全限定名中和方法参数中,分别表示子包和0到N个参数
常见的写法:
execution(* com.bigfong.test.service.*.*(..))
execution(* com.bigfong.test.service.*Service.*(..))
execution(* com.bigfong..service.*Service.*(..))
AOP配置三步: what-where-when
使用JDK动态代理开发AOP:
1.domain类;Employee
2.dao接口
public interface IEmployeeDAO {
void save(Employee emp);
void update(Employee emp);
}
dao实现类
public class EmployeeDAOImpl implements IEmployeeDAO {
@Override
public void save(Employee emp) {
System.out.println("保存数据");
}
@Override
public void update(Employee emp) {
System.out.println("修改数据");
}
}
3.service接口
public interface IEmployeeService {
void save(Employee emp);
void update(Employee emp);
}
service接口实现类
public class EmployeeServiceImpl implements IEmployeeService {
private IEmployeeDAO dao;
public void setDao(IEmployeeDAO dao) {
this.dao = dao;
}
@Override
public void save(Employee emp) {
dao.save(emp);
System.out.println("保存成功");
}
@Override
public void update(Employee emp) {
dao.update(emp);
throw new RuntimeException("出错啦");
}
}
四.模拟事务管理器
public class TransactionManager {
public void begin() {
System.out.println("开启事务");
}
public void commit() {
System.out.println("提交事务");
}
public void rollback() {
System.out.println("回滚事务");
}
}
五.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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="employeeDAO" class="com.bigfong.aopxml.dao.impl.EmployeeDAOImpl"/>
<bean class="com.bigfong.aopxml.service.impl.EmployeeServiceImpl">
<property name="dao" ref="employeeDAO"/>
</bean>
<!-- AOP配置:在什么地点+什么时机+做什么 -->
<!-- 1.WHAT,做什么增强 -->
<bean id="txManager" class="com.bigfong.aopxml.tx.TransactionManager"/>
<aop:config>
<!-- 配置AOP切面 -->
<aop:aspect ref="txManager"> <!-- 关联WHAT -->
<!-- 2.WHERE,在哪些包中的哪些类中的哪些方法上做增强 -->
<aop:pointcut id="txPoint" expression="execution(* com.bigfong.aopxml.service.*Service.*(..))" />
<!-- 3.WHEN,在方法执行的什么时机做增强 -->
<aop:before method="begin" pointcut-ref="txPoint"/>
<aop:after-returning method="commit" pointcut-ref="txPoint"/>
<aop:after-throwing method="rollback" pointcut-ref="txPoint"/>
</aop:aspect>
</aop:config>
</beans>
6.测试代码
@SpringJUnitConfig
public class App {
@Autowired
private IEmployeeService service;
@Test
public void testSave()throws Exception {
System.out.println(service.getClass());
service.save(new Employee());
}
@Test
public void testUpdate()throws Exception {
System.out.println(service.getClass());
service.update(new Employee());
}
}
使用CGLIB动态代理实现事务:
与使用JDK动态代理开发AOP类似,需要修改xml中<aop:config proxy-target-class="true">
各种时机的增强
1.aop:before(前置增强):在方法执行之前增强(应用:权限控制,日志等)
2.aop:after-returning(后置增强):在方法正常执行完成之后(在retrun返回值前)执行增强(中间没有遇到任何异常)(应用:提交事务、统计分析数据结果等)
3.aop:throwing(异常增强):在方法抛出异常退出时执行增强 (应用:回滚事务、记录日志的异常信息等)
4.aop:after(最终增强):在方法执行之后执行,相当于在finally里面执行skin以通过配置throwing来获得拦截到的异常信息 (应用:释放资源等)
5.aop:around(环绕增强):可以在方法调用前/后完成自定义的行业,有两个要求:
1)方法需要返回一个Object(返回结果)
2)方法的第一个参数必须是ProceedingJoinPoint(可以继续向下传递的连接点)
(应用:缓存、性能日志、权限、事务管理等)
增强丰富参数配置
1.在增强方法中获取异常信息
xml配置:
<aop:after-throwing method="roallback" pointcut-ref="txPoint" throwing="ex">
java代码:
public void rollback(){
System.out.println("回滚事务" + ex);
}
2.获取被增强方法信息,并传递给增强方法
Spring AOP提供org.aspectj.lang.JoinPoint类,作为增强方法的第一个参数。
JoinPoint:提供访问当前被增强方法的真实对象、代理对象、方法参数等数据
ProceedingJoinPoint:JoinPoint子类,只用于环绕增强中,可以处理被增强方法
使用JoinPoint方法
public void begin(JoinPoint jp){
代理对象:jp.getThis().getClass();
目标对像:jp.getTarget().getClass();
被增强方法参数:Arrays.toString(jp.getArgs());
当前连接点签名:jp.getSignature();
当前连接点类型:jp.getKind();
}
使用ProceedingJoinPoint类
public Object around(ProceedingJoinPoint pjp){
Object ret = null;
//开启事务
try{
//执行目标方法
ret = pjp.proceed();
//提交事务
}catch(Throwable ex){
//回滚事务
}finally{
//释放资源
}
return ret;
}
注解配置事务--默认使用JDK动态代理
1.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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- DI注解解析器 -->
<context:annotation-config/>
<!-- Ioc注解解析器 -->
<context:component-scan base-package="com.bigfong.aopanno"/>
<!-- AOP注解解析器 -->
<aop:aspectj-autoproxy/>
</beans>
2.实体类Employee
3.IEmployeeDAO接口类,EmployeeDAOImpl是对IEmployeeDAO的实现类,接口实现类需添加注解@Repository
public interface IEmployeeDAO {
void save(Employee emp);
void update(Employee emp);
}
@Repository
public class EmployeeDAOImpl implements IEmployeeDAO {
@Override
public void save(Employee emp) {
System.out.println("保存员工");
}
@Override
public void update(Employee emp) {
System.out.println("修改员工");
}
}
4.IEmployeeService接口类,EmployeeServiceImpl是对IEmployeeService的实现类,并添加注解@Service
public interface IEmployeeService {
void save(Employee emp);
void update(Employee emp);
}
@Service
public class EmployeeServiceImpl implements IEmployeeService {
@Autowired
private IEmployeeDAO dao;
@Override
public void save(Employee emp) {
dao.save(emp);
System.out.println("保存成功");
}
@Override
public void update(Employee emp) {
dao.update(emp);
throw new RuntimeException("程序出错啦");
}
}
5.模拟事务管理器
@Component
@Aspect //配置切面
public class TransactionManager {
//XML:<aop:pointcut id="txPoint" expression="execution(* com.bigfong.aopanno.service.*Service.*(..))" />
@Pointcut("execution(* com.bigfong.aopanno.service.*Service.*(..))")
public void txPoint() {
}
@Before("txPoint()") //相当于调用上面的方法
public void begin() {
System.out.println("开启事务");
}
@AfterReturning("txPoint()")
public void commit() {
System.out.println("提交事务");
}
@AfterThrowing(value="txPoint()",throwing="ex")
public void rollback(JoinPoint jp,Throwable ex) {
System.out.println("回滚事务,异常信息:"+ex.getMessage());
}
@After("txPoint()")
public void close() {
System.out.println("释放资源");
}
@Around("txPoint()")
public Object aroundMethod(ProceedingJoinPoint pjp) {
Object ret = null;
System.out.println("环绕增强:开启事务");
try {
ret = pjp.proceed();//调用真实对象的方法
System.out.println("环绕增强:正常执行真实方法");
}catch (Throwable e) {
System.out.println("环绕增强:回滚事务,异常信息:"+e.getMessage());
}finally {
System.out.println("环绕增强:释放资源");
}
return ret;
}
}
6.测试代码
@Autowired
private IEmployeeService service;
@Test
public void testSave()throws Exception {
System.out.println(service.getClass());
service.save(new Employee());
}
@Test
public void testUpdate()throws Exception {
System.out.println(service.getClass());
service.update(new Employee());
}
注解配置事务--使用CGLIB动态代理
1.xml配置:
<aop:aspectj-autoproxy proxy-target-class="true"/>
2.修改切入点静态式
@Pointcut("execution(* com.bigfong.aopanno.service..*(..))")
public void txPoint() {
}
上一篇:spring5整理:(六)CGLIB动态代理-模拟事务
下一篇:spring5整理:(八)DAO