好处:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能.
代理模型的三种方式
1. 静态代理
2. 动态代理
JDK动态代理
CGLIB动态代理
一、静态代理
在不修改目标对象方法的基础上,对目标对象方法进行扩展。
//Service接口 public interface UserService { void save(); void delete(); void update(); }
//Service实现 public class UserServiceImpl implements UserService { @Override public void save() { System.out.println("新增用户"); } @Override public void delete() { System.out.println("删除用户"); } @Override public void update() { System.out.println("修改用户"); } }
/** * 静态代理类 * 实现日志扩展功能 *要求: *1)和目标(类)实现同样的接口 *2)在静态代理类中传入目标对象实例,以便调用目标对象的方法 *3)可以在静态代理类的方法中添加代理逻辑代码 */ public class LogProxy implements UserService{ //接收目标对象实例 private UserService userService; //使用构造方法传入目标对象实例 public LogProxy(UserService userService){ this.userService = userService; } @Override public void save() { System.out.println("before=====save"); //调用目标对象的方法 userService.save(); System.out.println("after=====save"); } @Override public void delete() { System.out.println("before=====save"); //调用目标对象的方法 userService.delete(); System.out.println("after=====save"); } @Override public void update() { System.out.println("before=====save"); //调用目标对象的方法 userService.update(); System.out.println("after=====save"); } } package com.lemon.a_staticproxy; import com.lemon.service.UserService; import com.lemon.service.impl.UserServiceImpl; //测试 public class Test { public static void main(String[] args) { //使用静态代理模式 //1.创建目标对象 UserService userService = new UserServiceImpl(); //2.创建静态代理类对象 UserService proxy = new LogProxy(userService); //3.调用代理类的方法 proxy.save(); proxy.update(); proxy.delete(); } }
静态代理的缺点:
1)一个静态代理类只能代理一个目标类
2)静态代理类的每个方法都需要编写重复的代理逻辑,代码比较冗余
二、JDK动态代理
前提:目标对象有接口的情况
/** * 用于生成JDK动态代理对象的工具类 */ public class LogProxy { /** * 生成JDK动态代理对象的方法 * * 返回值:生成的JDK动态代理对象 * 参数:target, 传入目标对象 */ public static Object getProxy(Object target){ /** * 参数一:类加载器,JDK动态代理的底层使用类加载器来生成的一个动态类的。通常传入当前类的类加载器即可!!!(LogProxy.class.getClassLoader()) * 参数二:目标对象的接口列表(所有接口),通常使用目标对象获取接口列表(target.getClass().getInterfaces()) * 参数三:接口。 该用于编写 代理类的代理逻辑代码。通常我们要提供InvocationHandler接口的实现类(匿名内部类的方式提供) */ return Proxy.newProxyInstance( LogProxy.class.getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() { /** * invoke方法:用于编写 代理类的代理逻辑代码。 * invoke方法在什么时候会被调用? * 该方法会在调用JDK代理对象的每个方法的时候被执行!!!!! * * @param proxy: 生成JDK动态代理对象 * @param method: 目标对象的执行方法的对象 * @param args: 目标对象的方法参数列表 * @return 返回值:目标对象方法执行后的返回结果 * @throws Throwable */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //获取目标对象的方法名称 String methodName = method.getName(); System.out.println("before======"+methodName); //获取方法的参数列表 /* if(args!=null) System.out.println(Arrays.asList(args));*/ //调用目标对象的方法 /** * 参数一:执行的对象(必须传入目标对象,不能传入代理对象,否则会死循环) * 参数二:方法的参数列表 */ Object result = method.invoke(target,args); System.out.println("after======"+methodName); return result; } } ); } }
三、
目标对象可有可无
<!-- 导入spring-core(包含cglib依赖) --> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> </dependency> </dependencies>
/** * 演示JDK动态代理 */ public class Demo { public static void main(String[] args) { //1.创建目标对象 UserService userService = new UserServiceImpl(); //2.创建静态代理类对象 UserService proxy = (UserService) LogProxy.getProxy(userService); //3.调用代理类的方法 proxy.save(); proxy.delete(); proxy.update(); } }
/** * 用于生成Cglib代理对象的工具类 */ public class LogProxy { /** * 生成Cglib代理对象 * 返回值:生成的Cglib子类代理对象 * 参数:目标对象(目标对象没有接口) */ public static Object getProxy(Object target){ /** * 方法返回值:生成的Cglib子类代理对象 * 参数一:目标对象的类型(target.getClass()) (其实目标对象的类型就是Cglib代理对象 的 父类) * 参数二:MethodInterceptor接口,用于编写 代理对象的代理逻辑代码。通常提供MethodInterceptor接口的匿名内部即可 */ return Enhancer.create( target.getClass(), new MethodInterceptor() { /** * intercept方法:在调用代理对象的每个方法的时候会执行 * @param proxy: 生成的代理对象 * @param method: 目标对象的方法对象 * @param args: 目标对象的方法参数列表 * @param methodProxy: 代理对象的方法对象 * @return * @throws Throwable */ @Override public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { //获取目标对象的方法名称 String methodName = method.getName(); System.out.println("before======"+methodName); /** * 调用目标对象的方法 */ //方式一:直接使用目标对象 调用 目标对象的方法 Object result = method.invoke(target,args); //方式二:使用代理类(子类)调用 目标对象(父类)的方法 //invokeSuper: 调用父类的方法 //Object result = methodProxy.invokeSuper(proxy,args); System.out.println("after====="+methodName); return result; } } ); } }
/** * 演示CJLIB动态代理 */ public class Demo2 { public static void main(String[] args) { //1.创建目标对象 UserService userService = new UserServiceImpl(); //2.创建静态代理类对象 UserService proxy = (UserService) LogProxy.getProxy(userService); //3.调用代理类的方法 proxy.save(); proxy.delete(); proxy.update(); } }
*_* Spring aop 案例
1 、创建service接口和实现
public interface LemonService { void save(); void delete(); void update(); }
public class LemonServiceImpl implements LemonService { @Override public void save() { System.out.println("新增"); } @Override public void delete() { System.out.println("删除"); } @Override public void update() { System.out.println("修改"); } }
2、创建切面类
/** * 日志切面类 */ public class LogAspect { /** * 通知方法(插入到目标方法的前面) */ public void writeLog(){ System.out.println("before======="); } }
3、配置切面类
<?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: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/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 1.创建目标对象 --> <bean id="lemonService" class="com.lemon.service.impl.LemonServiceImpl"/> <!-- 2.创建切面对象 --> <bean id="logAspect" class="com.lemon.log.LogAspect"/> <!-- 3.切面配置 --> <aop:config> <!-- 切面配置 = 通知(advice)+切入点(pointcut)--> <!-- ref: 引用切面类对象 --> <aop:aspect ref="logAspect"> <!-- 定义切入点 --> <!-- id: 定义切入点的别名 expression: 切入点表达式(用于定义需要切入的方法) --> <aop:pointcut id="pt" expression="execution(* com.lemon.service.impl.LemonServiceImpl.*(..))"/> <!-- 定义通知 --> <!-- method: 使用切面类的哪个方法作为通知方法 pointcut-ref: 关联切入点 --> <!-- 前置通知 --> <aop:before method="writeLog" pointcut-ref="pt"/> </aop:aspect> </aop:config> </beans>