代理模式
模拟场景:
房东 黑中介 住户
任务:
房东:把房出租,换点零花钱
中介:帮房东出租房屋(最基本的任务),获取中介费
住户:有床就行
代理模式图:
代理的特点:
(1)代理对象和目标对象(被代理者)共同实现一个借口
(2)代理对象要完成自己本职的工作(给房东租房),还要完成自己的额外的操作(收中介费)
代理的意义:
在完成了自己的本职工作的时候,还完成了额外的扩展
具体代码实现:
package cn.tedu.pojo;
/**
* 定义接口,目标对象和代理对象都要实现这个接口
* @author tedu
*
*/
public interface Rent {
public void rent();
}
package cn.tedu.pojo;
/**
* 中介
* 完成买房子 还要收取费用
* @author tedu
*
*/
public class Proxy implements Rent {
//调用目标类 完成目标类的任务
private House house = new House();
@Override
public void rent() {
//完成目标类的任务
house.rent();
//完成自己的任务,实现扩展
System.out.println("代理类:收取中介费");
}
}
package cn.tedu.pojo;
/**
* 目标类 即房东出租房子
* @author tedu
*
*/
public class House implements Rent{
@Override
public void rent() {
System.out.println("目标类:房东出租房屋");
}
}
package cn.tedu.pojo;
/**
* 租房者 找代理类租房子
* @author tedu
*
*/
public class Client {
public static void main(String[] args) {
//创建代理类
Proxy proxy = new Proxy();
//租房子
proxy.rent();
}
}
静态代理:
用户调用代理类 代理类调用被代理类,并添加一些自己额外扩展的东西.
模拟场景:
使用分层,在添加用户的时候,实现事务的管理,事务的管理一般放在service层
具体方法:
(1)代理类和目标类实现同一个接口
(2)在静态代理类中调用目标类的方法,并添加控制
代码分析:
(1)使用注解,要配置xml文件 开启包扫描
(2)创建代理类 事务类
自己的理解:
就是创建一个静态代理类和目标类实现用一个接口,在接口里面调用事务,用户在调用的时候表面是调用的是目标对象,
实际上调用的是代理对象,当共同实现一个接口的时候会发生风险,所以更改类的id,修改静态代理的id名字,和目标类
的id名字.
//代码实现
//配置文件的写法:
<context:component-scan base-package="dao,service,servlet,tx,proxy"/>
----------
//事务类
package tx;
import org.springframework.stereotype.Component;
@Component
public class TransactionManager {
public void begin(){
System.out.println("事务开启");
}
public void commit(){
System.out.println("事务提交");
}
public void rollback(){
System.out.println("事务回滚");
}
}
----------
//servlet层
package servlet;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import service.UserService;
@Controller //id为userServlet
public class UserServlet {
@Autowired
private UserService userService;//面向切面编程
public void addUser(){
userService.addUser();
}
}
----------
//service层
package service;
public interface UserService {
public void addUser();
}
package service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import dao.UserDao;
@Service("target") //目标类 被代理的类
public class UserServiceImpl implements UserService{
@Autowired
private UserDao userDao;
@Override
public void addUser() {
userDao.addUser();
}
}
----------
//dao层
package dao;
public interface UserDao {
public void addUser();
}
package dao;
import org.springframework.stereotype.Repository;
@Repository
public class UserDaoImpl implements UserDao {
@Override
public void addUser() {
System.out.println("添加一个用户");
}
}
----------
//测试类
package text;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import service.UserService;
public class TestSpring {
@Test
public void Test(){
ApplicationContext context =
new ClassPathXmlApplicationContext("applicationContext.xml");
//调用的是代理类
UserService service =(UserService) context.getBean("userService");
service.addUser();
}
}
静态代理的优点和缺点:
优点:能够将业务处理和事务控制进行分类,实现解耦
缺点:(1)当添加的事务多的时候,代码大量重复,
(2)只能处理一类业务,如果要处理其他的业务,需要重新定义代理对象,代理对象不能通用,灵活性低
动态代理
动态代理分为:jdk动态代理和CgLib动态代理:
1.jdk动态代理
解决静态代理的不灵活性,可以添加多个业务
实现方法:
写一个静态代理方法传入,传入目标类和,要添加的事物
核心方法:Proxy.newProxyInstance(类加载器,获取所有的接口,new InvocationHandler(){
匿名内部类,重写invoke方法});
实现代码:
dao service servlet tx 层的代码和静态的一样
/**
* 动态代理
* @author acer
*
*/
public class DynamicProxy {
//为方法添加事物操作操作 //目标方法 //事物管理类
public static Object getProxy(final Object target,final TransactionManager tx){
Object proxy=Proxy.newProxyInstance(target.getClass().getClassLoader(),//类加载器
target.getClass().getInterfaces(), //所有的接口
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Object result = null;
System.out.println("动态代理开始");
tx.begin();
//反射执行目标方法
method.invoke(target, args);
tx.commit();
System.out.println("动态代理结束");
return result;
}
});
return proxy;
}
}
//测试类
package text;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import proxy.DynamicProxy;
import service.UserService;
import tx.TransactionManager;
public class TestString {
@Test
public void text01(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//获取目标对象
UserService target = (UserService) context.getBean("target");
System.out.println(target.getClass());
//获得事物类
TransactionManager tx = (TransactionManager) context.getBean("tx");
//通过动态代获取对象
UserService userService = (UserService) DynamicProxy.getProxy(target, tx);
userService.addUser();
}
}
2.CgLib动态代理:
和jdk动态代理的实现差不多
具体代码如下:
“`
package proxy;
import java.lang.reflect.Method;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import tx.TransactionManager;
/**
* cgblib动态代理
* @author tedu
*
*/
public class CgblibProxy {
public static Object getProxy(final Object target ,final TransactionManager tx){
//1.创建增强器,操作的是二进制
Enhancer enhancer = new Enhancer();
//2.设置父类
enhancer.setSuperclass(target.getClass());
//3.如果有接口就实现接口
enhancer.setInterfaces(target.getClass().getInterfaces());
//4.设置回调接口方法 //相当于jdk动态代理中的 new InvocationHandler()
enhancer.setCallback(new MethodInterceptor() {
@Override //jdk动态代理中的invoke()方法
public Object intercept(Object proxy, Method method, Object[] args,
MethodProxy arg3) throws Throwable {
Object result = null;
try{
System.out.println("cgblib动态代理开始");
tx.begin();//开启事务
result = method.invoke(target, args);
tx.commit();//提交事务
System.out.println("cgblib动态代理结束");
}catch(Exception e){
e.printStackTrace();
tx.rollback();//回滚事务
}
return result;
}
});
//返回代理对象
return enhancer.create();
}
}
```
jdk动态代理和CgLib动态代理的区别:
jdk动态代理需要目标对象必须和代理对象实现相同的接口,否则不能生成代理对象
jdk动态代理生成的代理对象速度快, .java到.class 运行速度慢
生成的代理对象为 class com.sun.proxy.$Proxy6
CgLib动态代理不需要目标对象实现接口也能生成代理对象,因为代理对象是目标对象的子类
CgLib动态代理生成代理对象的速度慢 .java到.calss到二进制 运行速度快,底层是二进制
生成的代理对象为 class service.UserServiceImpl$$EnhancerByCGLIB$$34a01bb6
jdk动态代理和CgLib动态代理的选择:
一般都使用jdk动态代理的多,在有特定的环境下才使用CgLib动态代理,如果目标对象没用使用接口就用CgLib动态代理;