Spring中的动态代理

1.Spring AOP 就是要对目标进行代理对象的创建, Spring AOP是基于动态代理的,有动态代理机制: JDK动态代理和CGLIB动态代理 

动态代理:在虚拟机内部,运行的时候,动态生成代理类(运行时生成,runtime生成) ,并不是真正存在的类,

一般格式:

Proxy$$ (Proxy$$Customer)

静态代理:实际存在代理类 (例如:struts2 Action的代理类 ActionProxy,struts2的拦截器)

JDK动态代理

JDK动态代理,针对目标对象的接口进行代理 ,动态生成接口的实现类 !(必须有接口)

1、 必须对接口生成代理

2、 采用Proxy类,通过newProxyInstance方法为目标创建代理对象。对目标对象接口进行代理

static ObjectnewProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
          返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。

该方法接收三个参数 :

  (1)目标对象类加载器

  (2)目标对象实现的接口

  (3)代理后的处理程序InvocationHandler,是代理实例的调用处理程序 实现的接口

方法返回:一个带有代理类的指定调用处理程序的代理实例,它由指定的类加载器定义,并实现指定的接口

3、 实现InvocationHandler 接口中 invoke方法,在目标对象每个方法调用时,都会执行invoke

代码实现:

针对接口进行代理:

//接口(表示代理的目标接口)
public interface ICustomerService {
	//保存
	public void save();
	//查询
	public int find();
}

//实现类
public class CustomerServiceImpl implements ICustomerService{

	public void save() {
		System.out.println("客户保存了。。。。。");
	}

	public int find() {
		System.out.println("客户查询数量了。。。。。");
		return 100;
	}
}

针对接口实现动态代理:

实现jdk动态代理方式1:
public class JdkProxyFactory{
	//成员变量
	private Object target;
	//注入target目标对象
	public JdkProxyFactory(Object target) {
		this.target = target;
	}
	
	public Object getProxyObject(){
		//参数1:目标对象的类加载器
		//参数2:目标对象实现的接口
		//参数3:回调方法对象
		/**方案一:在内部实现new InvocationHandler(),指定匿名类*/
		return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler(){

			public Object invoke(Object proxy, Method method, Object[] args)
					throws Throwable {
				//如果是保存的方法,执行记录日志操作
				if(method.getName().equals("save")){
					writeLog();
				}
				//目标对象原来的方法执行
				Object object = method.invoke(target, args);//调用目标对象的某个方法,并且返回目标对象方法的返回值
				return object;
			}
			
		});
	}
	
	//记录日志
	private static void writeLog(){
		System.out.println("增强代码:写日志了。。。");
	}

}

//实现动态代理的方式2:直接使用调用类作为接口实现类,实现InvocationHandler接口
public class JdkProxyFactory implements InvocationHandler{
	//成员变量
	private Object target;
	//注入target
	public JdkProxyFactory(Object target) {
		this.target = target;
	}
	
	public Object getProxyObject(){
		//参数1:目标对象的类加载器
		//参数2:目标对象实现的接口
		//参数3:回调方法对象
		/**方案三:直接使用调用类作为接口实现类,实现InvocationHandler接口*/
		return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),this);
	}
	
	//记录日志
	private static void writeLog(){
		System.out.println("增强代码:写日志了。。。");
	}

	//参数1:代理对象
	//参数2:目标的方法对象
	//参数3:目标的方法的参数
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		//如果是保存的方法,执行记录日志操作
		if(method.getName().equals("save")){
			writeLog();
		}
		//目标对象原来的方法执行
		Object object = method.invoke(target, args);//调用目标对象的某个方法,并且返回目标对象
		return object;
	}

}

测试:

 //目标:使用动态代理,对原来的方法进行功能增强,而无需更改原来的代码。
	//JDK动态代理:基于接口的(对象的类型,必须实现接口!)
	@Test
	public void testJdkProxy(){
		//target(目标对象)
		ICustomerService target = new CustomerServiceImpl();
		//实例化注入目标对象
		JdkProxyFactory jdkProxyFactory = new JdkProxyFactory(target);
		//获取Proxy Object代理对象:基于目标对象类型的接口的类型的子类型的对象
		ICustomerService proxy = (ICustomerService)jdkProxyFactory.getProxyObject();
		//调用目标对象的方法
		proxy.save();
		System.out.println("————————————————————————————————————————");
		proxy.find();
	}

JDK动态代理的缺点: 只能面向接口代理,不能直接对目标类进行代理 ,如果没有接口,则不能使用JDK代理。

CGLIB代理

Cglib代理的引入为了解决类的直接代理问题(生成代理子类),不需要接口也可以代理 !该代理方式需要相应的jar包,但不需要导入。因为Spring core包已经包含cglib ,而且同时包含了cglib 依赖的asm的包(动态字节码的操作类库)

1.针对没有接口的类直接代理:

//没有接口的类
public class ProductService {
	public void save() {
		System.out.println("商品保存了。。。。。");
		
	}

	public int find() {
		System.out.println("商品查询数量了。。。。。");
		return 99;
	}
}

2.使用cglib进行代理

public class CglibProxyFactory implements MethodInterceptor {

	private Object target;

	public CglibProxyFactory(Object target) {
		this.target = target;
	}

	public Object getProxyObject() {
		//代理对象生成器
		Enhancer enhancer = new Enhancer();
		
		//设置目标对象
		enhancer.setSuperclass(target.getClass());
		
		//设置回调函数
		enhancer.setCallback(this);	
		
		//创建返回代理对象
		Object object = enhancer.create();
		
		return object;

	}

	
	//回调方法(代理对象的方法)
	//参数1:代理对象
	//参数2:目标对象的方法对象
	//参数3:目标对象的方法的参数的值
	//参数4:代理对象的方法对象
	public Object intercept(Object proxy, Method method, Object[] args,
			MethodProxy methodProxy) throws Throwable {
		//如果是保存的方法,执行记录日志操作
		if(method.getName().equals("save")){
			writeLog();
		}
		//目标对象原来的方法执行
		Object object = method.invoke(target, args);//调用目标对象的某个方法,并且返回目标对象
		return object;
	}

// 增强代码
	public static void writeLog() {
		System.out.println("记录日志");
	}

}

测试:

//cglib动态代理:可以基于类(无需实现接口)生成代理对象
	@Test
	public void testCglibProxy(){
		//target目标:
		ProductService target = new ProductService();
		//weave织入,生成proxy代理对象
		//代理工厂对象,注入目标
		CglibProxyFactory cglibProxyFactory = new CglibProxyFactory(target);
		//获取proxy:思考:对象的类型
		//代理对象,其实是目标对象类型的子类型
		ProductService proxy=(ProductService) cglibProxyFactory.getProxyObject();
		//调用代理对象的方法
		proxy.save();
		System.out.println("————————————————————————————————————————");
		proxy.find();
		
	}

总结:

spring在运行期,生成动态代理对象,不需要特殊的编译器.

spring有两种代理方式:

    1.若目标对象实现了若干接口,spring使用JDK的java.lang.reflect.Proxy类代理。

    2.若目标对象没有实现任何接口,spring使用CGLIB库生成目标对象的子类。

使用该方式时需要注意:

   1.对接口创建代理优于对类创建代理,因为会产生更加松耦合的系统,所以spring默认是使用JDK代理。对类代理是让遗留系统或无法实现接口的第三方类库同样可以得到通知,这种方式应该是备用方案。

    2.标记为final的方法不能够被通知。spring是为目标类产生子类。任何需要被通知的方法都被复写,将通知织入。final方法是不允许重写的。

3.spring只支持方法连接点:不提供属性接入点,spring的观点是属性拦截破坏了封装。 面向对象的概念是对象自己处理工作,其他对象只能通过方法调用的得到的结果

  • 5
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Spring动态代理主要用于AOP(面向切面编程)的实现。AOP可以让我们在不改变原有代码的情况下,通过代理机制来增强方法的功能,比如日志记录、性能统计、事务处理等。Spring动态代理主要使用了JDK的动态代理和CGLIB动态代理两种技术。 1. JDK动态代理 JDK动态代理是通过反射和接口实现的代理,它只能代理实现了接口的类。在Spring,我们可以使用JDK动态代理来代理Service层的接口,从而实现AOP的功能。 首先定义一个接口: ``` public interface UserService { void addUser(User user); void deleteUser(int id); User getUser(int id); } ``` 然后定义一个切面类: ``` public class LogAspect { public void before() { System.out.println("方法执行前..."); } public void after() { System.out.println("方法执行后..."); } } ``` 接下来使用JDK动态代理来代理UserService: ``` public class UserServiceProxy implements InvocationHandler { private UserService target; private LogAspect logAspect; public UserServiceProxy(UserService target, LogAspect logAspect) { this.target = target; this.logAspect = logAspect; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { logAspect.before(); Object result = method.invoke(target, args); logAspect.after(); return result; } } ``` 最后,在Spring配置文件配置UserService的代理: ``` <bean id="userServiceProxy" class="com.example.UserServiceProxy"> <property name="target" ref="userService"/> <property name="logAspect" ref="logAspect"/> </bean> <bean id="userService" class="com.example.UserServiceImpl"/> <bean id="logAspect" class="com.example.LogAspect"/> ``` 2. CGLIB动态代理 CGLIB动态代理是通过继承实现的代理,它可以代理没有接口的类。在Spring,我们可以使用CGLIB动态代理来代理Dao层的实现类,从而实现AOP的功能。 首先定义一个实现类: ``` public class UserDaoImpl { public void addUser(User user) { System.out.println("添加用户..."); } public void deleteUser(int id) { System.out.println("删除用户..."); } public User getUser(int id) { System.out.println("获取用户..."); return new User(); } } ``` 然后定义一个切面类: ``` public class LogAspect { public void before() { System.out.println("方法执行前..."); } public void after() { System.out.println("方法执行后..."); } } ``` 接下来使用CGLIB动态代理来代理UserDaoImpl: ``` public class UserDaoImplProxy implements MethodInterceptor { private UserDaoImpl target; private LogAspect logAspect; public UserDaoImplProxy(UserDaoImpl target, LogAspect logAspect) { this.target = target; this.logAspect = logAspect; } public UserDaoImpl getProxy() { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(UserDaoImpl.class); enhancer.setCallback(this); return (UserDaoImpl) enhancer.create(); } @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { logAspect.before(); Object result = method.invoke(target, args); logAspect.after(); return result; } } ``` 最后,在Spring配置文件配置UserDaoImpl的代理: ``` <bean id="userDaoImplProxy" class="com.example.UserDaoImplProxy"> <property name="target" ref="userDaoImpl"/> <property name="logAspect" ref="logAspect"/> </bean> <bean id="userDaoImpl" class="com.example.UserDaoImpl"/> <bean id="logAspect" class="com.example.LogAspect"/> ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值