大家好,又见面了,我是你们的朋友全栈君。
定义
代理模式可以分为两种,一种是静态代理,一种是动态代理。 静态代理: 代理类一般会持有一个被代理的对象引用,且对于不关心的方法全部委托给被代理的对象处理。自己处理关心的方法。 这种代理方式是死板的,它不是在运行时动态创建,它就是硬编码,你代码编译前写的是什么,编译后就是什么。 换句话就是你按下CTRL S的那一刻,就会被代理对象生成一个不可动态改变的代理类。 静态代理一般对于代理的对象是单个或者多个固定的类(数量不会太多)使用。效果会比动态代理要好。 动态代理: 动态代理又分为JDK动态代理以及CGLIB动态代理。 JDK动态代理是实现一个InvocationHandler接口,并且调用Proxy的静态方法去产生代理类。主要是运行时动态生成代理类,类似CURD操作添加日志、拦截等等时使用。例如:Spring AOP。
举个栗子
代码语言:javascript复制以数据库连接为例,静态代理。
public interface Connection extends Wrapper {
/**
* 创建连接
* @return
* @throws SQLException
*/
Statement createStatement() throws SQLException;
/**
* 关闭连接
* @throws SQLException
*/
void close() throws SQLException;
}
代码语言:javascript复制public class DBUtil {
private static LinkedList<Connection> connectionList = new LinkedList<Connection>();
static{
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
private static Connection createNewConnection() throws SQLException {
return (Connection) DriverManager.getConnection("url","username", "password");
}
private DBUtil(){
if (connectionList == null || connectionList.size() == 0) {
for (int i = 0; i < 10; i ) {
try {
connectionList.add(createNewConnection());
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
public Connection getConnection() throws Exception{
if (connectionList.size() > 0) {
//这是原有的方式,直接返回连接,这样可能会被程序员把连接给关闭掉
//return connectionList.remove();
//下面是使用代理的方式,程序员再调用close时,就会归还到连接池
return new ConnectionStaticProxy(connectionList.remove());
}
return null;
}
public void recoveryConnection(Connection connection){
connectionList.add(connection);
}
public static DBUtil getInstance(){
return DataSourceInstance.dataSource;
}
private static class DataSourceInstance{
private static DBUtil dataSource = new DBUtil();
}
}
代码语言:javascript复制@Slf4j
public class ConnectionStaticProxy implements Connection {
private Connection connection;
public ConnectionStaticProxy(Connection connection) {
super();
this.connection = connection;
}
@Override
public Statement createStatement() throws SQLException {
return connection.createStatement();
}
@Override
public void close() throws SQLException {
log.info("不是真正关闭连接,只是归还给连接池");
DBUtil.getInstance().recoveryConnection(connection);
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
return null;
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return false;
}
}
代码语言:javascript复制动态代理 这个动态代理的演示只能代理Connection 这一个接口,如果出现这种情况,用静态代理会更好。 在你发现你使用静态代理的时候,需要写一大堆重复代码的时候,就请改用动态代理。
/**
* 描述: 动态代理
* 这里只是带来了一个类Connection
* 注:在你发现你使用静态代理的时候,需要写一大堆重复代码的时候,就请改用动态代理
*
*/
public class ConnectionProxy implements InvocationHandler {
private Connection connection;
public ConnectionProxy(Connection connection) {
super();
this.connection = connection;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 这里判断是Connection接口的close方法的话
if (Connection.class.isAssignableFrom(proxy.getClass()) && method.getName().equals("close")) {
// 我们不执行真正的close方法
//method.invoke(connection, args);
// 将连接归还连接池
DBUtil.getInstance().recoveryConnection(connection);
return null;
}else {
return method.invoke(connection, args);
}
}
public Connection getConnectionProxy(){
return (Connection) Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{Connection.class}, this);
}
}
代码语言:javascript复制当我们需要代理一系列类的某一些方法,最典型的应用就是springAOP,我们需要创造出一批代理类,切入到一系列类当中的某一些方法中。下面给出一个经常使用的动态代理方式。
/**
* 描述: 动态代理
* 这个代理类的作用是可以代理任何类,因为它被传入的对象是Object,而不再是具体的类
*
* @author: yanglin
* @Date: 2020-07-07-9:46
* @Version: 1.0
*/
@Slf4j
public class ConnectionProxyOne implements InvocationHandler {
private Object source;
public ConnectionProxyOne(Object source){
super();
this.source = source;
}
public void before(){
log.info("before 在方法前做一些事,比如打开事务");
}
public void after(){
log.info("after 在方法返回前做一些事,比如提交事务");
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 假设我们切入toString方法,其他其实也是类似的,一般我们这里大部分是针对特定的方法做事情的,通常不会对类的全部方法切入
// 比如我们常用的事务管理器,我们通常配置的就是对save,update,delete等方法才打开事务
if (method.getName().equals("toString")) {
before();
}
Object result = method.invoke(source, args);
if (method.getName().equals("toString")) {
after();
}
return result;
}
public Object getConnectionProxy(){
return Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{Connection.class}, this);
}
}
代码语言:javascript复制之前有个疑问?如果被代理的类未实现接口是否可以使用动态代理。答案是可以的。
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
log.info("接口的方法全部变成这样了");
// 这里source是TestClass,但是我们不能使用反射调用它的方法,像下面这样,放开这一行会抛异常
// return method.invoke(source, args);
/**
* 只要你确认你传入的类包括了所有你传入的接口的方法,只是没实现这些接口而已,那么你可以在invoke中这样使用
*/
log.info("before");
Method sourceMethod = source.getClass().getDeclaredMethod(method.getName(), method.getParameterTypes());
sourceMethod.setAccessible(true);
Object result = sourceMethod.invoke(source, args);
log.info("after");
return result;
}
以上,有兴趣的话可以跟下源码,理解下原理。(Proxy.newProxyInstance这是产生代理的入口)
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/160923.html原文链接:https://javaforall.cn