1、AOP 底层原理及使用动态代理
两种动态代理
第一种有接口情况,使用JDK动态代理
创建接口实现类的代理对象,增强类的方法
第二种没有接口情况,使用CGLIB动态代理
创建当前类子类的代理对象,增强类的方法
2、调用 newProxyInstance 方法
static Object newProxyInstance(ClassLoader loader, 类<?> interfaces, InvocationHandler h)
返回指定接口代理类的实例,该接口将方法调用分派给指定的调用处理程序
方法有三个参数:
第一参数:类加载器
第二参数: 增强方法所在的类, 这个类实现的接口,支持多个接口
第三参数:实现这个接口 InvocationHandler,创建代理对象,写增强的方法
3、编写JDK动态代理代码
可看proxy包
3.1 创建接口,定义方法
3.2 创建接口实现类,实现方法
public class JdkProxy {
public static void main(String[] args) {
/**
* static Object newProxyInstance(ClassLoader loader, 类<?> interfaces, InvocationHandler h)
* 返回指定接口代理类的实例,该接口将方法调用分派给指定的调用处理程序
*/
Class[] interfaces = {UserDao.class};
/* Proxy.newProxyInstance(JdkProxy.class.getClassLoader(), interfaces, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return null;
}
});*/
/**
* 1、可以直接使用匿名内部类
* 2、可以创建内部类实现InvocationHandler 重写方法
*/
UserDao userDao = new UserDaoImpl();
UserDao dao = (UserDao) Proxy.newProxyInstance(JdkProxy.class.getClassLoader(), interfaces, new InnerProxy(userDao));
int result = dao.add(100, 342);
System.out.println("result=" + result);
System.out.println("代理=" + dao);
}
}
class InnerProxy implements InvocationHandler {
private Object object;
public InnerProxy(Object object) {
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("方法之前执行,方法名=" + method.getName());
Object invoke = method.invoke(object, args);
System.out.println("方法之后执行了:" + object);
return invoke;
}
}
4、AOP术语
4.1 连接点
类里面可以被增强的方法叫做连接点
4.2 切入点
实际真正被增强的方法,称为切入点
4.3 通知(增强)
1、实际增强的逻辑部分称为通知
2、通知的类型
2.1、前置通知 @Before
2.2、后置通知 @After
2.3、环绕通知 @Around
@Around(value = "execution ( * com.example.demo.aop.aspect.User.run (..))")
public void around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕之前 ........... ");
joinPoint.proceed();
System.out.println("环绕之后 ........... ");
}
2.4、异常通知 @AfterThrowing
2.5、最终通知 @AfterReturning finally
无异常时执行顺序: 环绕之前 -> Before -> run -> AfterReturning -> After -> 环绕之后
有异常时执行顺序: 环绕之前 -> Before -> AfterThrowing -> After
4.4 切面
动作,把通知应用到切面点的过程
5、AOP操作
5.1 Spring框架中一般都是基于AspectJ实现AOP操作
1、什么是AspectJ
AspectJ不是Spring组成部分,独立AOP框架,一般把AspectJ和Spring框架一起使用,进行AOP操作
2、基于AspectJ实现AOP操作
1、 基于xml配置文件实现
2、 基于注解方式实现(使用)
3、在项目工程中引入AOP相关依赖
4、切入表达式
切入点表达式的作用: 知道对哪个类里面的哪个方法进行增强
5.2 语法结构:
execution: ( [方法返回类型] [返回路径] [类全路径] [方法名称] ([参数列表]) )
举例一:对 com.example.demo.aop.proxy.UserDao 里面的add 方法进行增强
execution: (* com.example.demo.aop.proxy.UserDao.add(…))
举例二:对 com.example.demo.aop.proxy.UserDao 里面的所有方法进行增强
execution: (* com.example.demo.aop.proxy.UserDao.(…))
举例三:对 com.example.demo.aop.proxy 包里面的所有类,以及类里面的所有方法进行增强
execution: ( com.example.demo.aop.proxy..(…))
6、AOP进行通知的配置
可以看aop/bean1.xml;/aop/aspect
<context:component-scan base-package="com.example.demo.aop.aspect"></context:component-scan>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
@Component
@Aspect
@Order(3)
public class UserProxy {
@Pointcut(value = "execution(* com.example.demo.aop.aspect.User.run(..))")
public void pointCutDemo(){
}
@Before(value = "pointCutDemo()")
public void before(){
System.out.println("Before ........... ");
}
@After(value = "execution(* com.example.demo.aop.aspect.User.run(..))")
public void after(){
System.out.println("After ........... ");
}
@AfterReturning(value = "execution(* com.example.demo.aop.aspect.User.run(..))")
public void afterReturning(){
System.out.println("AfterReturning ........... ");
}
@AfterThrowing(value = "execution(* com.example.demo.aop.aspect.User.run(..))")
public void afterThrowing(){
System.out.println("AfterThrowing ........... ");
}
@Around(value = "execution(* com.example.demo.aop.aspect.User.run(..))")
public void around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕之前 ........... ");
joinPoint.proceed();
System.out.println("环绕之后 ........... ");
}
}
6.1 在spring配置文件中,开启注解扫描
<context:component-scan base-package="com.example.demo.aop.aspect"></context:component-scan>
6.2 使用注解创建User和UserProxy对象
6.3 在增强类上添加注解@Aspect
@Component
@Aspect
public class UserProxy
6.4、在spring配置文件中,开启生成代理对象
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
7、AOP相同切入点抽取
使用 @Pointcut 进行抽取
@Pointcut(value = "execution(* com.example.demo.aop.aspect.User.run(..))")
public void pointCutDemo(){
}
@Before(value = "pointCutDemo()")
public void before(){
System.out.println("Before ........... ");
}
8、多个增强类对同一个方法进行增强,设置优先级
8.1 在增强类上面添加注解 @Order(数字类型值),数字值越小,优先级越高
@Order(1); 可看PersonProxy
不加order无异常时执行顺序: 环绕之前 -> Before -> run -> AfterReturning -> After -> 环绕之后
加order无异常时执行顺序:Person before -> 环绕之前 -> Before -> run -> AfterReturning -> After -> 环绕之后
@Component
@Aspect
@Order(1)
public class PersonProxy {
@Before(value = "execution(* com.example.demo.aop.aspect.User.run(..))")
public void before() {
System.out.println("Person before ...");
}
}
9、Aspectj 配置文件
可以看aop/bean2.xml;/aop/aspectXml
9.1 创建类,在类里面定义方法
9.2 创建增强类,编写增强逻辑
在增强类里面,创建方法,让不同方法代表不同通知类型
9.3 在 Spring 配置文件中配置切入点
<bean id="book" class="com.example.demo.aop.aspectXml.Book"></bean>
<bean id="bookProxy" class="com.example.demo.aop.aspectXml.BookProxy"></bean>
<aop:config>
<aop:pointcut id="pointC" expression="execution(* com.example.demo.aop.aspectXml.Book.buy(..))"/>
<aop:aspect ref="bookProxy">
<aop:before method="before" pointcut-ref="pointC"></aop:before>
</aop:aspect>
</aop:config>
public class BookProxy {
public void before(){
System.out.println("book before ...");
}
}
public class Book {
public void buy(){
System.out.println("buy ......");
}
}
10、完全注解开发
10.1 创建配置类,不需要配置xml文件开发
@ComponentScan(basePackages = “com.example.demo.aop”)
@EnableAspectJAutoProxy(proxyTargetClass = true) 替代
<aop:aspectj-autoproxy></aop:aspectj-autoproxy> 生成代理对象
@ComponentScan(basePackages = "com.example.demo.aop")
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class AllAnnoAspect {
}