On the JDK dynamic proxies (under) turn

Source: https://zhuanlan.zhihu.com/p/63126398

 

 

Introduction to JDK dynamic proxy, today and everyone to be a small case: an analog Spring's transaction management.

main content:

  • a familiar stranger
  • Cottage AOP transaction needs analysis
  • AOP transaction specific code implementation

a familiar stranger

If the interviewer asks, "Please talk about your understanding of Spring" and estimated that many people will blurt out: IOC and AOP. IOC is probably the most immediate impression on everyone Spring, is a large container, installed a lot of bean, it will help you to do dependency injection.

IOC

But for AOP, many people actually do not have much concept of time do not know where to use Spring AOP. As if the transaction with the cut, but not the specific understanding. Well, I ask you a question, I wrote a UserController, and UserServiceImpl implements UserService, and injecting Service layer objects in the UserController:

@Autowired
private UserService userService;

Well, this must be userService UserServiceImpl instance we write it?

If you do not understand what I want to ask, explain your own understanding of Spring's still too limited IOC.

In fact, Spring dependency injection is not necessarily an object is an instance of the class to write our own, it may be userServiceImpl proxy object. The following are two cases demonstrate this:

  • Object injection userServiceImpl

Injection type is UserServiceImpl

  • Injecting a proxy object userServiceImpl (CGLib dynamic proxies)

CGLib agent is injected dynamically generated proxy object userServiceImpl

 

Why two injections of different objects?

Because the second time I UserServiceImpl added @Transactional comment.

Spring at this time to read this comment, we know that we want to use transactions. And UserService class we write and does not contain any code related to the transaction. If you, how would you do? Dynamic proxies Well!

But to use the dynamic management agent to complete the transaction, but also you need to write a notification class, and the incoming notification object proxy object, in charge of affairs of the notification and submission open, and invoke the target object method of the same name to complete the business function within the proxy object.

We can think of the program, Spring certainly know. Similarly, Spring in order to achieve affairs, also wrote a notification class, TransactionManager. When creating dynamic proxy object proxy, Spring will transactionManager weaving proxy object, the proxy object is then injected into the UserController.

So userService we use in UserController in fact a proxy object, the proxy object only support transactions.


Cottage AOP transaction needs analysis

Spring understand the general process of the transaction, we come to analyze how to write their own affairs AOP a cottage.

AOP transaction, there are two concepts: AOP and transactions.

Affairs, everyone is familiar with, here mainly to talk about what is AOP. AOP, which is the abbreviation of Aspect-Oriented Programming (Oriented Programming) is. What is Aspect Oriented Programming? Sometimes directly introduce what a thing that may be more difficult. But it is doing when it comes, it will immediately take the hint of.

Our system, there is often a cross-business, such as transaction logs and so on. UserService of method1 use to it, BrandService of method2 have to use it. A cross cut into the business is to one aspect of the system. With a specific code shows that:

This aspect can be log, the transaction may be

Cross-business problem is the programming aspect-oriented programming. AOP's business goal is to make cross-modular. Section code can be moved around the original method:

When the original without AOP, cross-business written directly before and after the internal method, with the AOP cross traffic before and after the write method call. This is the underlying AOP implementations related to: the dynamic proxy method is actually the same name as the proxy object to call the target object, and add enhanced code before and after the call. However, the two eventually run effect is the same.

The so-called modular, my personal understanding is that the section of code to make a manageable state. Printing such as logging, it is no longer hard-coded in fragmented statement in the method, but made a notification class, to execute the code through a notification section.

So, now demand has been very clear that we need a notification class (TransactionManager) to perform a transaction, a proxy factory to help generate the proxy object, and then use various methods of dynamic agent transaction code woven into the proxy object.

Like following three Service, originally did not open transactions:

We hope to eventually achieve the effect, I added a @MyTransactional later, I returned to a proxy factory proxy object:

Acting factory using dynamic proxy to create a proxy object for each target audience

Detailed analysis:

txManager fact, before and after the target object test () method before and after the execution of a transaction, rather than internal method

That is, the proxy object transaction method = + target object method.

 

In addition, there is a thorny issue: the transaction, you must use the same Connection object. How to ensure? After the first obtain a Connection object from the data source and open transactions, deposit it in the current thread ThreadLocal, until a DAO layer, or taken from ThreadLocal, so we can guarantee the open Connection object Affairs and operation of the database using the same One.

After opening Affairs, Controller not directly call our own to write the Service, but Spring provides a proxy object

This is the realization of the principle of affairs.


AOP transaction specific code implementation

ConnectionUtils Tools

Package com.demo.myaopframework.utils; 

Import org.apache.commons.dbcp.BasicDataSource; 

Import the java.sql.Connection; 

/ ** 
 * connecting tools, which is used to obtain a connection from the data source, and implemented, and binding thread 
 * / 
public  class ConnectionUtils { 

    Private the ThreadLocal <connection> TL = new new the ThreadLocal <connection> (); 

    Private  static of BasicDataSource the dataSource = new new of BasicDataSource (); 

    // static code block, set the parameters of the database connection 
    static { 
        the dataSource. setDriverClassName ( "com.mysql.jdbc.Driver" ); 
        dataSource.setUrl ("JDBC: MySQL: // localhost: 3306 / Test" ); 
        dataSource.setUsername ( "the root" ); 
        dataSource.setPassword ( "123456" ); 
    } 


    / ** 
     * Get the current thread connection 
     * @return 
     * / 
    public getThreadConnection connection () {
         the try {
             // the start ThreadLocal 1. Get 
            connection Conn = tl.get ();
             // 2. determining whether the current thread connection 
            iF (Conn == null ) {
                 // 3. data from obtaining a source connection, and stored in a ThreadLocal 
                Conn =  dataSource.getConnection ();
                tl.set (Conn); 
            } 
            // 4. return connection on the current thread 
            return Conn; 
        } the catch (Exception E) {
             the throw  new new a RuntimeException (E); 
        } 
    } 

    / ** 
     * connecting thread and unbundling 
     * / 
    public  void removeConnection () { 
        tl.remove (); 
    } 
}

AOP notice (Transaction Manager)

Package com.demo.myaopframework.utils; 

/ ** 
 * transaction management and related tools, which contains, open transaction, committing the transaction, and transaction rollback releasable connection 
 * / 
public  class the TransactionManager { 

    Private ConnectionUtils connectionUtils; 

    public  void setConnectionUtils (connectionUtils connectionUtils) {
         the this .connectionUtils = connectionUtils; 
    } 

    / ** 
     * open transaction 
     * / 
    public   void beginTransaction () {
         the try { 
            connectionUtils.getThreadConnection () the setAutoCommit (. to false ); 
        } the catch (Exception E) { 
            e.printStackTrace ();
        } 
    } 

    / ** 
     * commit transaction 
     * / 
    public   void the commit () {
         the try { 
            connectionUtils.getThreadConnection () the commit ();. 
        } The catch (Exception E) { 
            e.printStackTrace (); 
        } 
    } 

    / ** 
     * rollback transaction 
     * / 
    public   void ROLLBACK () {
         the try { 
            connectionUtils.getThreadConnection () ROLLBACK ();. 
        } the catch (Exception E) { 
            e.printStackTrace (); 
        }
    } 


    / ** 
     * releasable connection 
     * /
    public   void Release () {
         the try { 
            connectionUtils.getThreadConnection () Close ();. // is also connected back to the pool 
            connectionUtils.removeConnection (); 
        } the catch (Exception E) { 
            e.printStackTrace (); 
        } 
    } 
}

Custom annotation

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTransactional {
}

Service

public interface UserService {
    void getUser();
}

 
public class UserServiceImpl implements UserService {
    @Override
    public void getUser() {
        System.out.println("service执行...");
    }
}

Examples factory

public  class the BeanFactory { 

    public Object the getBean (String name) throws Exception {
         // get the object target class Class 
        Class clazz = <?> the Class.forName (name);
         // get the target object 
        Object the bean = clazz.newInstance ();
         / / get @MyTransactional comment on the target class 
        MyTransactional myTransactional = clazz.getAnnotation (MyTransactional. class );
         // if hit @MyTransactional notes, return proxies, otherwise the target object 
        IF ( null ! = myTransactional) { 
            the ProxyFactoryBean the ProxyFactoryBean = new newThe ProxyFactoryBean (); 
            the TransactionManager txManager = new new the TransactionManager (); 
            txManager.setConnectionUtils ( new new ConnectionUtils ());
             // assembly and notification audiences 
            proxyFactoryBean.setTxManager (txManager); 
            proxyFactoryBean.setTarget (the bean); 
            Object proxyBean = proxyFactoryBean.getProxy ( );
             // return proxies 
            return proxyBean; 
        } 
        // return the target object 
        return the bean; 
    } 
}

Acting factory

public  class the ProxyFactoryBean {
     // notify 
    Private the TransactionManager txManager;
     // audiences 
    Private Object target; 

    public  void setTxManager (the TransactionManager txManager) {
         the this .txManager = txManager; 
    } 

    public  void setTarget (Object target) {
         the this .target = target; 
    } 

    / / incoming audience target, it notifies assembled, the proxy object returned 
    public Object The getProxy () { 
        Object proxy = the Proxy.newProxyInstance (
                target.getClass (). getClassLoader (), / * 1. classloader* / 
                Target.getClass (). The getInterfaces (), / * 2. interfaces implemented by the target object * / 
                new new of InvocationHandler () { / * 3.InvocationHandler * / 
                    @Override 
                    public Object Invoke (Object Proxy, Method, Method, Object [] args) throws the Throwable {
                         the try {
                             // 1. open transaction 
                            txManager.beginTransaction ();
                             // 2. perform operations 
                            Object retVal =Method.invoke (target, args);
                             // 3. commit the transaction 
                            txManager.commit ();
                             // 4. return result 
                            return retVal; 
                        } the catch (Exception E) {
                             // 5. The rollback transaction 
                            txManager.rollback ();
                             the throw  new new a RuntimeException (E); 
                        } the finally {
                             // 6. The releasable connection 
                            txManager.release (); 
                        } 

                    } 
                } 
        ); 
        return Proxy; 
    } 

}

Code structure

Get ordinary UserService:

UserServiceImpl @MyTransactional to add annotations, proxied:

 

Guess you like

Origin www.cnblogs.com/myseries/p/12501143.html