AOP is faceted programming. Aspect Oriented Programming

Faceted programming refers to the dynamic insertion of a piece of code into the original method code while the program is running. This is called faceted programming. Use a case to reflect the underlying principle of AOP a simple calculation function plus diary

Diary tools

public class LogUtils { public static void logBefore(String method, Object... Args) {system.out.println (" indicates change. "+ arrays.asList (args)); } public static void logAfterReturning(String method, Object result) {system.out.println (" method: "+ method + ". The return value is: "+ result); }}Copy the code

The calculator class

public class Calculator implements Calculate { @Override public int add(Integer num1, Integer num2) { LogUtils.logBefore("add", num1,num2); int result = num1 + num2; LogUtils.logAfterReturning("add", result); return result; } @Override public int add(Integer num1, Integer num2, Integer num3) { LogUtils.logBefore("add", num1,num2,num3); int result = num1 + num2 + num3; LogUtils.logAfterReturning("add", result); return result; } @Override public int div(Integer num1, Integer num2) { LogUtils.logBefore("div", num1,num2); int result = num1 / num2; LogUtils.logAfterReturning("div", result); return result; }}Copy the code

Use the JDK Dynamic Proxy Unified Diary Calculator

public class Calculator implements Calculate { @Override public int add(Integer num1, Integer num2) { int result = num1 + num2; return result; } @Override public int add(Integer num1, Integer num2, Integer num3) { int result = num1 + num2 + num3; return result; } @Override public int div(Integer num1, Integer num2) { int result = num1 / num2; return result; }} public class LogUtils {public static void logBefore(String method, Object... Args) {system.out.println ("前 提 出 : method name: "+ method + ". The arguments are: "+ arrays.asList (args)); } public static void logAfter(String method, Object... Args) {system.out.println (" after notification: method name: "+ method + ". The arguments are: "+ arrays.asList (args)); Public static void logAfterReturning(String method, Object result) {system.out.println (" return return: "); " + method + ". The return value is: "+ result); } public static void logAfterThrowing(String method, Exception e) {system.out.println (" " " + method + ". The exception message is: "+ e); }} public class JdkProxyFactory {// Resolve all classes by creating a proxy object, Public static Object createProxy(Object Target) {/** ** The first argument is the class loader of the proxy target Object <br/> * the second argument is the interface that the proxy Object needs to implement <br/> * The third argument is InvocationHandler implementation class */ return proxy.newProxyInstance (target.getClass().getClassLoader(), Target.getclass ().getinterfaces (), new InvocationHandler() {// Every time a proxy object calls a method, Public Object Invoke (Object proxy, Method Method, Object [] args) throws Throwable {/ / pre notice = = = > > > target method = = = = > > > post notice = = = = > > > return notice prior notice / / = = = > > > target method = = = = > > > post notice = = = = > > > notification Object result = null; Logutils.logbefore (method.getName(), args); Result = method.invoke(target, args); } finally {// after logutils.logAfter (method.getName(), args); } / / return notice LogUtils. LogAfterReturning (method. The getName (), result); } the catch (Exception e) {/ / Exception notification LogUtils logAfterThrowing (method. The getName (), e); } return result; }}); } public static void main(String[] args) { Calculate calculate = new Calculator(); Calculate proxy = (Calculate) createProxy(calculate); System.out.println(proxy.add(100, 200)); System.out.println("==============================="); System.out.println(proxy.div(100, 0)); }}Copy the code

Pros: This approach has solved all of our previous journaling needs. Very flexible. And can be convenient in the late maintenance and upgrade. Cons: Of course with JDK dynamic proxies, you need interfaces. If there is no interface. You cannot use JDK dynamic proxies.

Advice, the technical term for AOP programming, is enhanced code. Such as pre-enhanced code. Post-enhanced code. Exception enhancement code. These are called notification aspects and the classes that have the notification code in them are called aspects. Crosscutting Concerns Crosscutting concerns are where we can add enhancement code. Like the front position, the back position, the exception position. And the return value position. These are all called crosscutting concerns. The Target object is the object of attention. Or the proxied object. Proxy Objects that are created to intercept target object methods are called Proxy objects. Joinpoint a Joinpoint is the connection between a crosscutting concern and program code. Pointcuts are join points that users actually work with, called pointcuts. In Spring point by org. Springframework. Aop) Pointcut interface is described, it USES as a condition of the join query classes and methods.

AOP terminology:



Use Spring to implement AOP simple aspect programming

Create a Java project:



Import jar package:

Com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar Commons logging – 1.1.3. Jar spring aop — 4.3.18. The jar Spring – aspects – 4.3.18. RELEASE. Jar spring – beans – 4.3.18. The jar spring – the context – 4.3.18. The jar Spring – the core – 4.3.18. RELEASE. Jar spring – expression – 4.3.18. The jar

Configuration file information:

<? The XML version = "1.0" encoding = "utf-8"? > <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd "> <! <context:component-scan base-package="com"></context:component-scan> <! <aop:aspectj-autoproxy></aop:aspectj-autoproxy> </beans>Copy the code

Bean object

@Component public class Calculator implements Calculate { @Override public int add(Integer num1, Integer num2) { int result = num1 + num2; System.out.println(" target method add executed "); return result; } @Override public int add(Integer num1, Integer num2, Integer num3) { int result = num1 + num2 + num3; System.out.println(" target method add executed "); return result; } @override public int div(Integer num1, Integer num2) {system.out.println (" target method div was executed "); int result = num1 / num2; return result; }} @aspect @component public class LogUtils {/** * @before (value="execution(public int) Calculator.com.pojo.calculator.add (Integer, Integer))") public static void logBefore() {system.out.println (" calculator.calculator.add (Integer, Integer))") Method name: XXXX. The parameter is: args"); }}Copy the code

Test code:

Public class SpringTest {@test public void test1() throws Exception {// Create the Spring IOC container object ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml"); Calculate calculate = (Calculate) applicationContext.getBean("calculator"); calculate.add(100, 100); }}Copy the code

Proxy objects in Spring Aspect In Spring, you can proxy objects with interfaces and objects without interfaces. There are some subtle differences in use.

  1. If the proxied object implements the interface. When retrieving an object, the interface must receive the returned object.

  2. If the proxied object, if no interface is implemented. Use the object type itself when getting objects



    Spring’s pointcut expression

    The syntax for the @pointcut PointCut expression is:

    Fully qualified name of method of return value type (list of parameter types)

execution(public int com.atguigu.pojo.Calculator.add(Integer, Integer))

1) Matches a class name, any or more methods. Execution (public int com. Atguigu. Pojo. Calculator. (Integer, Integer)) means to match any of the method

2) In Spring, only public permission can be intercepted and access permission can be omitted.

execution(int com.atguigu.pojo.Calculator.*(Integer, Integer))

You can omit public access

3) Matches any type of return value, which can be represented by *

execution(public * com.atguigu.pojo.Calculator.*(Integer, Integer))

Indicates that any return value type can be accepted

4) Match any subpackages.

execution(public int com.atguigu.*.Calculator.add(Integer, Integer))

Matches any subpackages

5) Any type of parameter

execution(public int com.atguigu.pojo.Calculator.add(Integer, *))

Indicates that the second argument is of any type

… 1) Package execution(public int com… 2) package execution(public int com… Pojo.calculator.add (Integer, Integer)) means that there can be any level of packages between COM and POJO.

2) any type of parameter execution (public int com. Atguigu. Pojo. Calculator. The add (…). ) does not care about the number of arguments and the type of arguments.

Fuzzy matching: // any return value, any method full qualifier, any argument execution(* (…)) Execution (. (…)) // Execution (…) )

An exact match: execution (public int com. Atguigu. Aop. Calculator. The add (int, int))

Angle expressions connect: &&, | | / / said needs at the same time satisfy the two expressions @ Before (” execution (public int com. Atguigu. Aop. Calculator. The add (int, int)), “+” &”

  • “Execution (public * com. Atguigu. Aop. Calculator. The add (…). ) “)

// Indicates that only one of the two conditions needs to be satisfied, Will be matched to the @ Before (” execution (public int com. Atguigu. Aop. Calculator. The add (int, Int)) + “|” “|” + “execution (public * com. Atguigu. Aop. Calculator. A * (int))”)

Spring notifications are executed in the following order: Normal: pre notice = = = = > > > > target method = = = = > > > > post notice = = = = = > > > > after the return value Exceptions: front notice = = = = > > > > target method = = = = > > > > rear notice = = = = = > > > > throw exceptions

@aspect @component public class LogUtils {/** * @before (value = “execution(public int) com.atguigu.pojo.Calculator. (Integer, Integer))”) // Public static void logBefore() {system.out.println (” logBefore: method name: XXXX. The parameter is: args “); Rear} / * * * @ After notification / @ After (value = “execution (public int com. Atguigu. Pojo. Calculator. (an Integer, Integer))”) public static void logAfter() {system.out.println (” The arguments are: “); * * *} / return value to inform / @ AfterReturning (value = “execution (public int com. Atguigu. Pojo. Calculator. (an Integer, Integer))”) public static void logAfterReturning() {system.out.println ( The return value is: “); Abnormal} / * * * notification / @ AfterThrowing (value = “execution (public int com. Atguigu. Pojo. Calculator. (an Integer, Integer) “) public static void logAfterThrowing() {system.out.println (” The exception message is: “); }

}

Obtaining JoinPoint information JoinPoint is the information about the JoinPoint. You only need to add a JoinPoint parameter to the parameters of the notification method. You can get information about the interception method.

Note: is org. The aspectj. Lang. JoinPoint class – this class.

/ * * * @ Before is front-facing notification * / @ Before (value = "execution (public int com. Atguigu. Pojo. Calculator. * (Integer, Integer))") public static void logBefore(JoinPoint jp) {public static void logBefore(JoinPoint JP) {Copy the code

// jp.getSignature().getName() gets the method name // jp.getargs () gets the parameter passed by the target method system.out.println (” pre-notification: method name: “+ jp.getSignature().getName() +” The arguments are: “+ arrays.aslist (jp.getargs ())); }

There are two steps to get the return value of the intercepting method: 1. Add Object result to the returning method; 2. Add parameter RETURNING = “parameter name” to @afterRETURNING

The <br/> * RETURNING attribute sets which parameter to use to receive returned values */ @afterRETURNING (value = "execution(public int)) com.atguigu.pojo.Calculator.*(Integer, Integer))",returning="result") public static void logAfterReturning(JoinPoint jp, Object result) {system.out.println (" return notification: method name: "+ jp.getSignature().getName() + ". The return value is: "+ result); }Copy the code

Throwing = “Parameter name”; throwing= “parameter name”; throwing= “parameter name”; throwing= “parameter name”;

/** * throwing="e" */ @afterthrowing (value =" execution(public int) com.pojo.Calculator.*(Integer, Integer) ",throwing="e") public static void logAfterThrowing(JoinPoint JP,Throwable e) {system.out.println (" " + jp.getSignature().getName() + ". The exception message is: "+ e); }Copy the code

Spring’s wrap notification uses the @around annotation. 2. If the circular notification is executed at the same time as other notices. Circular notifications take precedence over other notifications. 3. A wrap notification must have a return value (wrap if there is no return value. Subsequent notifications will not receive the result of the target method execution. 4. In the surround notification. If an exception is intercepted. Make sure you throw it out. Otherwise, other exception notifications cannot catch exceptions.

/** * @throwable * @around annotation indicates that wrap notification <br/> * 1 wrap notification takes precedence over normal notification (default)<br/> * 2 Wrap notification methods must have a return value. And the return value must be the return value of the target method. * 3 After receiving an exception in the surround notification method, */ @around (value = "execution(public int com.pojo.calculator.*(Integer, Integer))") public static Object around(ProceedingJoinPoint pjp) throws Throwable { Object result = null; Try {try {system.out.println (" surround the front notification "); // Call the target method result = pjp.proceed(); } finally {system.out.println (" surround after notification "); } system.out. println(" wrap around return notification: "+ result); } catch (Throwable e) {system.out.println (" looped exception notification: "+ e); throw e; } return result; }}}}}}Copy the code

Reuse of Pointcut expressions * Reuse of Pointcut expressions * Step 1: Define a method * Step 2: On methods, define a Pointcut expression using @pointcut * Step 3: Replace Pointcut expressions with method calls where they need to be reused

/** * Reuse of pointcut expressions * Step 1: Define a method <br/> Methodically, define a Pointcut expression using @pointcut <br/> * Step 3: */ @pointcut (value="execution(public int com.pojo.calculator.*(Integer, Integer))") public static void pointcut1() {} /** * @before */ @before (value = "pointcut1()") // Pointcut1 === pointcut1() Static void logBefore(JoinPoint JP) {system.out.println (" "+ jp.getSignature().getName() + ". The arguments are: "+ arrays.aslist (jp.getargs ())); }Copy the code

Order of execution of multiple notifications

When we have multiple facets, multiple notifications:

1. By default, the order in which notifications are executed is determined by the alphabetical order of the aspect class.

2. Use the @ORDER annotation on the facet class to determine the Order in which the notifications are executed (smaller values, earlier).





How do I configure AN AOP program based on XML

<? The XML version = "1.0" encoding = "utf-8"? > <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd "> <! <context:component-scan base-package="com"></context:component-scan> <! <bean id="calculator" class="com.pojo. calculator" /> <! Class ="com.util.LogUtils" /> <aop:config> <! Aop :aspect ref="logUtils"> <! Aop :pointcut expression="execution(public int); aop:pointcut expression="execution(public int) com.pojo.Calculator.*(Integer, Integer))" id="pointcut1"/> <! Pointcut expression <br/> --> < AOP :before method="logBefore" pointcut="execution(public)  int com.pojo.Calculator.*(Integer, Integer))"/> <! <br/> pointcut expression <br/> --> < AOP :after method="logAfter" pointcut ="pointcut1"/> <! -- AOP: RETURNING notification ="result" Sets which parameter in returning notification to receive return values. --> <aop:after-returning method="logAfterReturning" pointcut-ref="pointcut1" returning="result" /> <! -- AOP :after-throwing method="logAfterThrowing" -- aop:after-throwing method="logAfterThrowing" pointcut-ref="pointcut1" throwing="e" /> </aop:aspect> </aop:config> </beans>Copy the code

Spring data access engineering environment construction

Create a Java project

Import jar package:

Commons logging - 1.1.3. Jar druid - 1.1.9. Jar mysql connector - Java - 5.1.37 - bin. Jar spring aop -- 4.3.18. The jar Spring beans - 4.3.18. RELEASE. Jar spring - the context - 4.3.18. The jar spring - core - 4.3.18. The jar Spring - expression - 4.3.18. RELEASE. Jar spring - JDBC - 4.3.18. The jar spring - the orm - 4.3.18. The jar Spring - test - 4.3.18. RELEASE. Jar spring - tx - 4.3.18. The jarCopy the code

Jdbc.properties property configuration file:

url=jdbc:mysql://localhost:3306/book
user=root
password=root
driverClassName=com.mysql.jdbc.Driver
initialSize=5
maxActive=10
Copy the code

Applicationcontext.xml configuration file:

<? The XML version = "1.0" encoding = "utf-8"? > <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd "> <! -- load the JDBC. The properties attribute configuration file - > < context: the property - placeholder location = "classpath: JDBC. Properties" / > <! - configure the database connection pool - > < bean id = "dataSource" class = "com. Alibaba. Druid. Pool. DruidDataSource" > < property name = "username" value="${user}" /> <property name="password" value="${password}" /> <property name="driverClassName" value="${driverClassName}" /> <property name="url" value="${url}" /> <property name="initialSize" value="${initialSize}"  /> <property name="maxActive" value="${maxActive}" /> </bean> <! - configured jdbcTemplate template class = = is dedicated to the operation of database - > < bean id = "jdbcTemplate" class = "org. Springframework. JDBC. Core. JdbcTemplate" > <property name="dataSource" ref="dataSource" /> </bean> </beans>Copy the code

The JdbcTemplate wrapper class that provides JDBC in Spring is called JdbcTemplate. It can be very convenient to help us execute SQL statements, operation database.

Prepare the database data for a single table

drop database if exists jdbctemplate; create database jdbctemplate; use jdbctemplate; CREATE TABLE `employee` ( `id` int(11) primary key AUTO_INCREMENT, `name` varchar(100) DEFAULT NULL, 'salary' decimal(11,2) DEFAULT NULL); Insert into ` employee ` (` id `, ` name `, ` salary `) values (1, 'lee three', 5000.23), (2, 'bill', 4234.77), (3, 'Cathy', 9034.51). (4, 'zhao 6, 8054.33), (5,' hole 7, 6039.11), (6, 'eight cao, 7714.11); select * from employee;Copy the code

Experiment 2: Update the salary field of the record with ID =5 to 1300.00

@test public void test2() throws Exception {String SQL = “Update employee set salary =? Where id =?” ; jdbcTemplate.update(sql, new BigDecimal(1300),5); }

Experiment 3: Batch insertion

@Test public void test3() throws Exception { String sql = "insert into employee(`name`,`salary`) values(? ,?) "; // Insert a record corresponding to a one-dimensional array // jdbctemplate.update (SQL, "new", new BigDecimal(10000000)); List<Object[]> args = new ArrayList<Object[]>(); args.add(new Object[] {"aaaa",new BigDecimal(11111)}); args.add(new Object[] {"bbbb",new BigDecimal(22222)}); jdbcTemplate.batchUpdate(sql, args); }Copy the code

Experiment 4: Query the database record with ID =5 and return it as a Java object

public void test4() throws Exception { String sql = "select id,name,salary from employee where id = ?" ; /** * RowMapper interface, The BeanPropertyRowMapper is responsible for converting each query row into a javaBean object <br/> * BeanPropertyRowMapper needs to convert the query result into an Employee<br/> * queryForObject to query an object <br/> */ Employee employee = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<Employee>(Employee.class), 5); System.out.println( employee ); }Copy the code

Mysql > select salary>4000 from salary database, salary>4000 from List

public void test5() throws Exception { String sql = "select id,name,salary from employee where salary > ?" ; /** * RowMapper interface, Is responsible for converting each query row into a javaBean object <br/> * BeanPropertyRowMapper is responsible for converting each query row to Employee< BR /> */ List<Employee> List = jdbcTemplate.query(sql, new BeanPropertyRowMapper<Employee>(Employee.class), new BigDecimal(4000)); System.out.println( list ); }Copy the code

Experiment 6: Query maximum salary

@Test public void test6() throws Exception { String sql = "select max(salary) from employee"; / / queryForObject query returns only one row of the statement / / query query returns more rows of SQL BigDecimal maxSalary = jdbcTemplate. QueryForObject (SQL, BigDecimal.class); System.out.println( maxSalary ); }Copy the code

Experiment 7: Insert an employee record using an SQL statement with named parameters and pass in the parameter values as a Map


\

Test code:

Public void test7() throws Exception {/** * : Name is a placeholder. SQL = "insert into employee(' name ', 'salary') values(:name, :salary)"; Map<String, Object> paramMap = new HashMap<>(); Parammap. put("name", "name") ); paramMap.put("salary", new BigDecimal(10000)); namedParameterJdbcTemplate.update(sql, paramMap); }Copy the code

Experiment 8: Repeat experiment 7, passing in parameter values as SqlParameterSource

Public void test8() throws Exception {/** * : Name is a placeholder. SQL = "insert into employee(' name ', 'salary') values(:name, :salary)"; Employee employee = new Employee(null, "xxxx", new BigDecimal(1234)); namedParameterJdbcTemplate.update(sql, new BeanPropertySqlParameterSource(employee)); }Copy the code

Experiment 9: Create a Dao and automatically assemble a JdbcTemplate object

@repository public class EmployeeDao {@autoWired JdbcTemplate JdbcTemplate; public int saveEmployee(Employee employee) { String sql = "insert into employee(`name`,`salary`) values(? ,?) "; return jdbcTemplate.update(sql, employee.getName(),employee.getSalary()); }}Copy the code

Test code:

@test public void test9() throws Exception {employeeDao. SaveEmployee (new Employee(null, "I entered once ", new BigDecimal(12342))); }Copy the code

Experiment 10: Create a Dao for a JdbcTemplate by inheriting JdbcDaoSupport

@Repository public class EmployeeDao extends JdbcDaoSupport{ // @Autowired // JdbcTemplate jdbcTemplate; @autoWired public void setDataSource2(DataSource DataSource) {system.out.println ("setDataSource2: " + dataSource ); setDataSource(dataSource); } public int saveEmployee(Employee employee) { String sql = "insert into employee(`name`,`salary`) values(? ,?) "; return getJdbcTemplate().update(sql, employee.getName(),employee.getSalary()); }}Copy the code