This is the 12th day of my participation in Gwen Challenge

Spring AOP belongs to the second generation of AOP, which adopts dynamic proxy mechanism and bytecode generation technology. And initial AspectJ adopt the compiler will crosscutting logic woven into the target object is different, the dynamic proxy mechanism and bytecode generation are at run time as target to generate a proxy object, and weave crosscutting logic to this proxy objects and eventually use is woven into the crosscutting logic proxy objects, rather than a true target object.

To understand this difference and what can be achieved, it is necessary to start with the root of dynamic proxy: the proxy pattern

Proxy patterns for design patterns

Agent between visitors and visitors, can isolate the direct interaction between the two, visitors to deal with the agent as if in dealing with visitors in, because the agent is almost solely with the function of agents usually, agent can handle the access request is unnecessarily bother visitors to deal with. From this point of view, the agent can reduce the burden of the visitor. In addition, even if the agent ultimately forwards the access request to the actual visitor, it can add specific logic before or after the request, such as secure access restrictions or, like a real estate agent, a fee for the placement.

In proxy mode, four roles are typically involved, as shown in the figure below.

ISubject: This interface is an abstraction of the visited or accessed resource

SubjectImpl: The concrete implementation class of the visited or accessed resource

SubjectProxy: The proxy implementation class of the interviewedor interviewedresource that holds a specific instance of the ISubject interface. In this scenario, we will be brokering SubjectImpl, and the SubjectProxy will now hold the instance of SubjectImpl.

Client: Represents the abstract role of the visitor

Both SubjectImpl and SubjectProxy implement the same interface ISubject, while SubjectProxy internally holds a reference to SubjectImpl. When a Client requests a service through Request (), SubjectProxy forwards the request to SubjectImpl. From this point of view, SubjectProxy is unnecessary. However, the SubjectProxy does more than just forward requests, and more often adds additional access restrictions to requests. The direct invocation relationship between SubjectImpl and SubjectProxy is shown below:

Before or after the request is forwarded to the subject object, additional processing logic can be inserted, such as logging the start time of method execution before the forward and the end time after the forward, to check the time of request() execution in the subject object.

Spring AOP is essentially implemented using this proxy mechanism, but the implementation details are different, and let’s see why.

Suppose to all request in the system () method to intercept, between 0 to 6 PM the next day at midnight every day, call the request was not accepted, so, we should provide a ServiceControlSubjectProxy SubjectImpl, to add the crosscutting logic, ServiceControlSubjectProxy are defined as follows:

public class ServiceControlSubjectProxy implements ISubject {
    private ISubject subject;
    public ServiceControlSubjectProxy(ISubject s){
        this.subject = s;
    }
    public String request(a){
        TimeOfDay startTime = new TimeOfDay(0.0.0);
        TimeOfDay endTime = new TimeOfDay(5.59.59);
        TimeOfDay currentTime = new TimeOfDay();
        if (currentTime.isAfter(startTime) && currentTime.isBefore(endTime))
            return null;
        
        String originalResult = subject.request();
        return "Proxy:"+originalResult; }}Copy the code

After that, we use the ServiceControlSubjectProxy SubjectImpl instead use, as shown in the following code:

ISubject target = new SubjectImpl();
ISubject finalSubject = new ServiceControlSubjectProxy(target);
finalSubject.request();
// The access control logic is already included in the request processing logic
Copy the code

However, the system may not necessarily have request() methods for ISubject implementation classes. The IRequestable interface and implementation classes may also have Request () methods, which are also concerns that need to be crosscutting. IRequestable and its implementation class code are as follows:

public interface IRequestable{
    void request(a);
}
/ / implementation class
public class RequestableImpl implements IRequestable{
    public void request(a){
        System.out.println("request processed in RequestableImpl"); }}Copy the code

In order for the IRequestable implementation class to weave in the above cross-cutting logic, we have to provide the corresponding proxy object as follows:

public class ServiceControlRequestProxy implements ISubject {
    private IRequestable requestable;
    public ServiceControlRequestProxy(IRequestable requestable){
        this.requestable = requestable;
    }
    public void request(a){
        TimeOfDay startTime = new TimeOfDay(0.0.0);
        TimeOfDay endTime = new TimeOfDay(5.59.59);
        TimeOfDay currentTime = new TimeOfDay();
        if (currentTime.isAfter(startTime) && currentTime.isBefore(endTime))
            return; requestable.request(); }}Copy the code

And bind the proxy object instead of the target object to the system as follows:

IRequestable target = new RequestableImpl();
IRequestable proxy = new ServiceControlRequestProxy(target);
proxy.request();
Copy the code

See the problem? Although JoinPoint is the same (request() method execution), the corresponding target object type is different. We implement a separate proxy object for different target object types. In practice, the crosscutting logic to be added to these proxy objects is the same. When there are hundreds of Pointcut matching target objects of different types in the system, we create hundreds of proxy objects for those target objects!

This method of creating static proxies for the corresponding target object is feasible in principle, but there are problems in the specific application, so look for other methods to avoid the dilemma just encountered…

A dynamic proxy

Using this mechanism, we can dynamically generate proxy objects for the specified interface during system runtime, helping us out of the initial dilemma of using a static proxy implementation.

The realization of the dynamic Proxy mechanism is mainly composed of a class and an interface, or Java. Lang. Reflect. The Proxy class and Java. Lang. Reflect. InvocationHandler interface. Let’s take a look at how dynamic proxies can be used to implement the previous “Request service time control” feature. Although we provide proxy objects for both ISubject and IRequestable, since the crosscutting logic to be added to the proxy object is the same, all we need to do is implement an InvocationHandler, which is defined as follows:

public class RequestCtrlInvovationHandler implements InvocationHandler{
	private Object target;
    public RequestCtrlInvovationHandler(Object target){
        this.target = target;
    }
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{
        if (method.getName.equals("request")){
            TimeOfDay startTime = new TimeOfDay(0.0.0);
            TimeOfDay endTime = new TimeOfDay(5.59.59);
            TimeOfDay currentTime = new TimeOfDay();
            if (currentTime.isAfter(startTime) && currentTime.isBefore(endTime))
            	return null;
            
            return method.invoke(target, args);
        }
        return null; }}Copy the code

Then, we can use the Proxy class, according to the logic of RequestCtrlInvovationHandler, for ISubject and IRequestable two types to generate the corresponding Proxy object instance, the code is as follows:

ISubject subject = (ISubject)Proxy.newProxyInstance(
	ProxyRunner.class.getClassLoader(),
    new Class[](ISubject.class),
    new RequestCtrlInvovationHandler(new SubjectImpl()));
subject.request();

IRequestable requestable = (IRequestable)Proxy.newProxyInstance(
	ProxyRunner.class.getClassLoader(),
    new Class[](IRequestable.class),
    new RequestCtrlInvovationHandler(new RequestableImpl()));
requestable.request();
Copy the code

Dynamic proxies are good, but they don’t meet every need. Dynamic proxy can only be used for classes that implement interfaces. If a class does not implement any Interface, it cannot use dynamic proxy to generate dynamic proxy objects. While interface oriented programming should be the preferred practice, it does not rule out other programming practices. For target objects that do not implement any Interface, we need to find other ways to dynamically generate proxy objects for them.

By default, Spring AOP uses a dynamic proxy mechanism to generate proxy object instances for target objects if it finds that the corresponding Interface is implemented. If the target object does not implement any Interface, Spring AOP tries to generate dynamic proxy object instances for the target object using an open source dynamic bytecode Generation Library called CGLIB (Code Generation Library).

Dynamic bytecode generation

Using dynamic bytecode generation technology is the principle of extended object behavior, we can inherit the extension of target object, to generate the corresponding subclasses, and subclasses can be extended by overriding the behavior of the parent class, as long as the implementation of crosscutting logic into subclasses, and then let the system using the extended a subclass of the target object, can achieve the same effect as the proxy pattern.

However, extending the object definition by inheritance does not allow the static proxy model to create separate subclasses for each different type of target object. Therefore, we need to use the dynamic bytecode generation library such as CGLIB to dynamically generate the corresponding extended subclasses for the target object during the system running.

To demonstrate the use of CGLIB and what can be achieved, we define the following target classes:

public class Requestable{
    public void request(a){
        System.out.println("rq in Requestable without implement any interface"); }}Copy the code

Extensions to the Requestable class, first of all need to implement a net. Sf. Additional. Proxy. The Callback. But more often, we can directly use net. Sf. Additional. Proxy. The MethodInterceptor interface (MethodInterceptor expanded the Callback interface).

public class RequestCtrlCallback implements MethodInterceptor{
    public Object intercept(Object object, Method method, Object[] args, MethodProxy proxy) throws Throwable{
        if (method.getName.equals("request")){
            TimeOfDay startTime = new TimeOfDay(0.0.0);
            TimeOfDay endTime = new TimeOfDay(5.59.59);
            TimeOfDay currentTime = new TimeOfDay();
            if (currentTime.isAfter(startTime) && currentTime.isBefore(endTime))
            	return null;
            
            return proxy.invokeSuper(object, args);
        }
        return null; }}Copy the code

Thus, RequestCtrlCallback implements the logic for controlling access to the request() method request. Now we need to dynamically generate a subclass for the target object via CGLIB’s enhancer and append the crosscutting logic from RequestCtrlCallback to the subclass as follows:

Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Requestable.class);
enhance.setCallback(new RequestCtrlCallback());

Requestable proxy = (Requestable)enhancer.create();
proxy.request();
Copy the code

By specifying the parent class for the subclass to be generated for enhancer, as well as the Callback implementation, enhancer finally generates the proxy object instance for us.

The only limitation of extending classes with CGLIB is that final methods cannot be overridden.