Preface
There are many ways to distinguish between dynamic agent and static agent , But it’s not about the essence
This article mainly looks at this problem from the application layer from top to bottom
Application scenarios
Static proxy
// Data preparation
Order order = new Order();order.setUserId(1);
// Create a static agent
OrderServiceStaticProxy orderServiceStaticProxy = new OrderServiceStaticProxy();
// Use static agent to save order number operation , Static agent agent saveOrder Method
orderServiceStaticProxy.saveOrder(order);
A dynamic proxy
// Data preparation
Order order = new Order();order.setUserId(2);
// Create dynamic proxy objects
IOrderService orderProxy = (IOrderService) new ServiceDynamicProxy(new OrderServiceImpl()).bind();
// Use dynamic agent to save order number operation
orderProxy.saveOrder(order);
More application scenarios of dynamic agents
Look at the code difference
// Create a static agent
OrderServiceStaticProxy orderServiceStaticProxy = new OrderServiceStaticProxy();
// Create dynamic proxy objects
IOrderService orderProxy = (IOrderService) new ServiceDynamicProxy(new OrderServiceImpl()).bind();
myth ( Not exactly ): Static proxies are not interface oriented programming , prove :
Modify static proxy code
public class StaticProxy {
private Object target;
public StaticProxy(Object target) {
this.target = target;
}
public Object bind() {
return target;
}
public int saveOrder(Order order) throws Throwable {
IOrderService service;
if (target instanceof IOrderService) {
service = (IOrderService) target;
} else {
throw new RuntimeException(" The target class does not support this method ");
}
// Omit enhance logic
return service.saveOrder(order);
}
}
Modified static agent application layer code , With dynamic agents As like as two peas , It will be mentioned below that when a proxy class is used to proxy multiple classes , The essence of the interface oriented feature of static proxy is existence bug Of
IOrderService orderProxy = (IOrderService) new StaticProxy(new OrderServiceImpl()).bind();
Essential difference 1. Expand the agency Method How difficult is it
1.1 Proxy the same class , And represent only one method
Dynamic agents and static agents have about the same amount of code , But dynamic agents are less efficient , Because dynamic agents Proxy.newProxyInstance() Will generate class File overhead is high .
1.2 Proxy the same class , There are many ways to expand agency
Static proxy
public class StaticProxy {
private Object target;
public StaticProxy(Object target) {
this.target = target;
}
public Object bind() {
return target;
}
public int saveOrder(Order order) throws Throwable {
IOrderService service;
if (target instanceof IOrderService) {
service = (IOrderService) target;
} else {
throw new RuntimeException(" The target class does not support this method ");
}
// Omit enhance logic
return service.saveOrder(order);
}
/**
* Add a method to the proxy class that needs to be proxied , Every time you add a method , I'm going to write another method with the same name
*
*/
public int deleteOrder(Order order) throws Throwable {
IOrderService service;
if (target instanceof IOrderService) {
service = (IOrderService) target;
} else {
throw new RuntimeException(" The target class does not support this method ");
}
// Omit enhance logic
return service.deleteOrder(order);
}
}
The realization of dynamic agent
It is very convenient to proxy all methods of a class , Set up before and after Method .
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
beforeMethod(argObject);
// Methods and parameters obtained by reflection
Object object = method.invoke(target, args); // The original logic of the surrogate class , Get the return value of
afterMethod();
return object;
}
If you only proxy a few methods of a class , There are two realizations
The first one is
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("XXX")) {
// before after
} else if...
else {
Object object = method.invoke(target, args); // The original logic of the surrogate class , Get the return value of
}
return object;
}
The second kind
Containers can be written in classes , It’s better to write it in the configuration file
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ( Containers . Is there a (method.getName())) {
// before after
} else {
Object object = method.invoke(target, args); // The original logic of the surrogate class , Get the return value of
}
return object;
}
Summary : Extend multiple methods of the same class , Dynamic agents are much better than static agents
Essential difference 2. Expand the agency class How difficult is it
2.1 The same proxy class proxies multiple proxied classes
Static proxy –> instanceof Judge to avoid misuse , Copy multiple methods ,bind() There will be Bug
public Object bind() {
return target;
}
public int saveOrder(Order order) throws Throwable {
IOrderService service;
if (target instanceof IOrderService) {
// Type check required
service = (IOrderService) target;
} else {
throw new RuntimeException(" The target class does not support this method ");
}
return result;
}
/**
* Add a proxy class StorageService
*
*/
public int reduceStorage(Order order) throws Throwable {
StorageService service;
if (target instanceof StorageService) {
service = (StorageService ) target;
} else {
throw new RuntimeException(" The target class does not support this method ");
}
// Omit enhance logic
return service.reduceStorage(order);
}
IOrderService orderProxy = (IOrderService) new StaticProxy(new OrderServiceImpl()).bind();
// The realization of hidden danger
StaticProxy staticProxy = (StaticProxy) new StaticProxy(new OrderServiceImpl()).bind();
staticProxy.reduceStorage(); // This statement is exposed to the caller , Call and throw exception
A dynamic proxy –> Confirm the interface to be proxy according to the parameters ,bind() The interface passed in by the method determines how many classes need to be proxied
/**
* Directly through the incoming interface Class object , Add agent class
*/
public Object bind(){
// Get the... Of the proxy class class file
Class cls = target.getClass();
ClassLoader classLoader = cls.getClassLoader();
Class[] interfaces = cls.getInterfaces(); // It can also be passed in as a parameter
return Proxy.newProxyInstance(classLoader, interfaces,this);
}
The proxy class returned by the above dynamic proxy , Eliminate the hidden danger of static proxy . because Dynamic proxy is the proxy interface , Static proxies are concrete classes , If you want an agent , Proxy two classes , There are hidden dangers in static proxy .jdk The premise of eliminating hidden danger by dynamic proxy is , Both classes are interface implementation classes . If not , use cglib
Dynamic agents can compensate for jdk
The limitations of dynamic agents .
/**
* invoke It's the routing of all the proxy methods , Don't do it instanceof The validity check of
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("XXX")) {
// before after
} else if...
else {
Object object = method.invoke(target, args); // The original logic of the surrogate class , Get the return value of
}
return object;
}
Summary : Dynamic proxy can add the proxy class when the application layer passes in parameters , There is no big difference between the complexity of business logic , The key is to expand the ability of agent class , Dynamic agents are much better than static agents
Advantages of dynamic agents
1. Proxy a single object only in business logic , And enhance the way
The implementation of static agent is complex , Dynamic agents concentrate code on filter Can finish
// filter Stop the swearing in the comments , agent request
HttpServletRequest proxyInstance = (HttpServletRequest) Proxy.newProxyInstance(
req.getClass().getClassLoader(),
new Class[]{
HttpServletRequest.class}, // Agent incoming servlet Of request
new InvocationHandler() {
// Anonymous Proxy implementation
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("getParameter")) {
String value = (String) method.invoke(req, args); // req yes filter Intercepted request
// Remove all spaces , Pay attention to the return value
value = value.replace(" ", "");
// Replace the swearing with... According to the length * Number , list It's a collection of stored swearing
for (String s : list) {
if (value.contains(s)) {
StringBuilder temp = new StringBuilder();
for (int i = 0; i < s.length(); i++) {
temp.append("*");
}
value = value.replace(s, temp.toString());
}
}
return value;
}
return method.invoke(req, args);
}
});
2. The method can be dealt with in depth
Static proxy
The internal implementation of the method of the proxied class cannot be modified
public class StaticProxy {
private Connection connection;
public StaticProxy(Connection connection) {
this.connection = connection;
}
public Object bind(){
return connection;
}
/**
* Cannot modify... Within agent close() Internal implementation
*/
public void close() throws SQLException {
beforeMethod();
connection.close();
afterMethod();
}
}
A dynamic proxy –> Extract parameters , Modify the internal implementation
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
if (method.getName().equals("close")) {
before();
if(method.getParameterCount() == 2) {
result = method.invoke(target, args[0], null); // Shield in parameter
}
if(method.getParameterCount() == 3) {
// Modify the internal implementation
result = new AnotherInst();
}
after(args[args.length - 1]);
return result ;
} else {
return method.invoke(target, args);
}
}
3. No adapter is needed to proxy the entire class
Static proxy –> Proxy entire class ( Database connection ), It’s expensive
If you want an agent MySQL Of Connection, You also need to copy the implementation into the proxy class , It’s a very complicated process
// Turn into Connection The object of , It takes a lot of money to use it normally , It's equivalent to introducing an adapter
proxyConnection = (Connection) proxy;
A dynamic proxy
// Agent class created by builder mode
proxy = new ConnectionProxy.Builder(connection)
.buildConnectionPool(this)
// Put your own interface with Connection Interface fusion , Give it together jdk Dynamic proxy agent
.buildAimClass(Connection.class, MyInterface.class)
.build();
// Turn into Connection The object of , Can be used for jdbc Of api
proxyConnection = (Connection) proxy;