java几种代理模式的实现方式
1. 代理模式
代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。简单的说就是,我们在访问实际对象时,是通过代理对象来访问的,代理模式就是在访问实际对象时引入一定程度的间接性,因为这种间接性,可以附加多种用途。
2. 静态代理
简单来说代理模式就是将被代理类包装起来然后重新实现相同的方法,并且调用原来方法的同时可以在方法前后添加一个新的处理。而这种包装可以使用继承或者组合来使用。当我们调用的时候需要使用的是代理类的对象来调用而不是原来的被代理对象。
静态代理有两种实现方式:
1、基于继承的方式实现
2、基于接口方式实现
基于继承实现静态代理
通过继承被代理对象,重写被代理方法,可以对其进行代理。
优点:被代理类无需实现接口
缺点:只能代理这个类,要想代理其他类,要想代理其他类需要写新的代理方法。
cglib动态代理就是采用这种方式对类进行代理。不过类是由cglib
帮我们在内存中动态生成的。
/**
* 被代理对象
*/
class UserDao{
public int insert(){
System.out.println("执行insert......");
return 1;
}
}
/**
* 代理对象
*/
class UserDaoProxy extends UserDao{
@Override
public int insert() {
System.out.println("代理执行方法前......");
int result = super.insert();
System.out.println("代理执行方法后......");
return result;
}
}
public class StaticProxyWithExtendsDemo {
public static void main(String[] args) {
UserDao userDao = new UserDaoProxy();
userDao.insert();
}
}
基于接口方式实现
- 被代理类与代理类一定要实现同一接口。
- 代理类需要将该接口作为属性,实例化时需要传入该接口的对象。
- 优点:可以代理所有实现接口的类。
- 缺点:被代理的类必须实现接口。
- JDK动态代理就是采用的这种方式实现的。同样的代理类是由JDK自动帮我们在内存生成的。
interface IUserDao{
void insert();
}
/**
* 被代理对象
*/
class UserDaoImpl implements IUserDao{
@Override
public void insert() {
System.out.println("这里执行了insert方法......");
}
}
/**
* 代理对象
*/
class UserDaoImplProxy implements IUserDao{
private IUserDao userDao;
public UserDaoImplProxy(IUserDao userDao){
this.userDao = userDao;
}
@Override
public void insert() {
System.out.println("代理执行方法前.....");
userDao.insert();
System.out.println("代理执行方法后.....");
}
}
public class StaticProxyWithInterfaceDemo {
public static void main(String[] args) {
//需要被代理的对象
IUserDao userDao = new UserDaoImpl();
//获取代理对象
IUserDao userDaoProxy = new UserDaoImplProxy(userDao);
//代理对象执行方法
userDaoProxy.insert();
}
}
3.动态代理
动态代理其实本质还是 将被代理类包装一层,生成一个具有新的相同功能的代理类。
但是与静态代理不同的是,这个代理类我们自己定义的。而动态代理这个代理类是根据我们的提示动态生成的。
相比于静态代理,动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类中的方法。
实现动态代理有几种方案:
- JDK动态代理
- CGLIB动态代理
3.1 JDK动态代理
通过java提供的Proxy类帮我们创建代理对象。
优点:可以生成所有实现接口的代理对象
缺点:JDK反射生成代理必须面向接口, 这是由Proxy的内部实现决定的。生成代理的方法中必须指定实现类的接口,它根据这个接口来实现代理类生成的所实现的接口。
interface IStudentDao{
void insert();
}
//被代理对象
class StudentDaoImpl implements IStudentDao{
@Override
public void insert() {
System.out.println("执行了insert方法......");
}
}
class ProxyInvoke implements InvocationHandler{
//需要被代理的对象
Object target;
public ProxyInvoke(Object target){
this.target = target;
}
/**
* nvoke方法的三个参数
* Object proxy:这个就是代理对象
* Method method:执行的方法的反射对象
* Object[] args:执行方法的参数
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理执行方法前......");
method.invoke(target,args);
System.out.println("代理执行方法后......");
return null;
}
}
public class DynamicProxyWithJDKDemo {
public static void main(String[] args) {
//需要被代理的对象
IStudentDao studentDao = new StudentDaoImpl();
//通过JDC的Proxy.newProxyInstance进行生成代理对象
// 参数一: 被代理类对象
// 参数二:接口类对象 被代理对象所实现的接口
// 参数三:调用处理器。 被调用对象的那个方法被调用后该如何处理
IStudentDao studentDaoProxy = (IStudentDao) Proxy.newProxyInstance(studentDao.getClass().getClassLoader(),studentDao.getClass().getInterfaces(),new ProxyInvoke(studentDao));
studentDaoProxy.insert();
}
}
如果业务逻辑不复杂,也可以进行简写:
public class DynamicProxyWithJDKDemo {
public static void main(String[] args) {
//需要被代理的对象
IStudentDao studentDao = new StudentDaoImpl();
IStudentDao studentDaoProxy = (IStudentDao)Proxy.newProxyInstance(studentDao.getClass().getClassLoader(), studentDao.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理执行方法前......");
Object invoke = method.invoke(studentDao, args);
System.out.println("代理执行方法后......");
return invoke;
}
});
studentDaoProxy.insert();
}
}
interface IStudentDao{
void insert();
}
class StudentDaoImpl implements IStudentDao{
@Override
public void insert() {
System.out.println("执行了insert方法......");
}
}
3.2 CGLib动态代理
CGLib(Code Generate Library) 与JDK动态代理不同的是,cglib生成代理是被代理对象的子类。因此它拥有继承方法实现静态代理的优点:不需要被代理对象实现某个接口。
缺点:不能给final类生成代理,因为final类无法拥有子类。使用cglib生成代理类也很简单,只要指定父类和回调方法即可
首先需要引入依赖
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.12</version>
</dependency>
public class DynamicProxyWithCgLibDemo {
public static void main(String[] args) {
TeacherDao teacherDao = new TeacherDao();
TeacherDao teacherProxy = (TeacherDao) Enhancer.create(teacherDao.getClass(), new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("执行方法之前");
proxy.invokeSuper(obj,args);
System.out.println("执行方法之后");
return null;
}
});
teacherProxy.insert();
}
}
class TeacherDao{
public void insert(){
System.out.println("执行了insert方法");
}
}