Exploring 2.6w words to explain Spring in detail

Table of contents

Getting Started with Spring

Overview

Basic applications of Spring

1.IoC

2.Spring’s core container (IoC container)

3.Spring entry program

4.DI

5. Dependency injection example (taking setter method injection as an example)

Beans in Spring

Bean configuration

Bean instantiation

Constructor instantiation

Case presentation

Bean scope

Example

Bean life cycle

Bean assembly (injection) method

XML configuration file assembly

Annotation assembly

Java configuration class assembly

automatic assembly

SpringAOP

The core concepts of AOP

dynamic proxy

JDK dynamic proxy

CGLIBAgent

AOP implementation

AspectJ development

element and its children

Detailed introduction to AspectJ annotations

XML-based declarative AspecJ

Annotation-based declarative AspecJ

Spring database development

Spring data development

Spring JDBC

Spring JdbcTemplate

Spring transaction management

XML-based declarative transaction management

Declarative transaction management based on Annotation


Getting Started with Spring

Overview

The Spring framework is a layered JavaEE enterprise-level open source framework organized and developed by Rod Johnson.

Basic applications of Spring

1.IoC

IoC (Inversion of Control) Inversion of Control

When using an object, the object generated by active new is converted into an object provided by "external". In this process, the control of object creation is transferred from the program to the outside. This idea is called inversion of control.

Spring technology implements IoC ideas

Spring provides a container, called the IoC container, which serves as the "outside" of the IoC idea.

The IoC container is responsible for a series of tasks such as object creation and initialization. The created or managed objects are collectively called Beans in the IoC container.

2.Spring’s core container (IoC container)

The main functions of the Spring framework are implemented through its core container. The Spring framework provides two core containers, namely BeanFactory and ApplicationContext.

BeanFactory

BeanFactory is the basic IoC container, which provides complete IoC service support. Simply put, BeanFactory is a factory that manages beans. It is mainly responsible for initializing various beans and calling their life cycle methods.

The BeanFactory interface provides collection implementation classes. The most commonly used one is XmlBeanFactory, which assembles beans according to the definitions in the XML configuration file.

When creating a BeanFactory instance, you need to provide detailed configuration information of the container managed by Spring. This information is usually managed in the form of XML files. The syntax for loading configuration information is as follows:

BeanFactory beanFactory=new XmlBeanFactory(new FileSystemResource("F:/applicationContext.xml"));

This loading method is not commonly used in actual development, so you can learn about it here.

ApplicationContext

ApplicationContext is a sub-interface of BeanFactory, also known as application context, and is another commonly used Spring core container. It not only contains all the functions of BeanFactory, but also adds support for internationalization, resource access, event propagation, etc.

There are usually two methods to create an ApplicationContext interface instance, as follows.

//1.通过ClassPathXmlApplicationContext创建,ClassPathXmlApplicationContext会从类路径classPath寻找指定的XML配置文件
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(String configLoactino);
//2.通过FileSystemXmlApplication创建,FileSystemXmlApplicationContext会从指定的文件系统路径(绝对路径)中寻找指定的XML配置文件
ApplicationContext applicationContext = new FileSystemXmlApplicationContext(String configLoactino);

Usually in Java projects, the ClassPathXmlApplicationContext class is used to instantiate the ApplicationContext container. In a Web project, the instantiation of the ApplicationContext container will be completed by the Web server.

After creating the Spring container, you can obtain the beans in the Spring container. Spring usually uses the following two methods to obtain Bean instances.

Object getBean(String name): Obtain the specified Bean based on the id or name of the Bean in the container. Forced type conversion is required after acquisition;

<T> T getBean(Class<T> requiredType): Get a Bean instance with a more class type. Since this method is a generic method, there is no need to perform cast type conversion after obtaining the Bean.

3.Spring entry program

1) Create a maven project in Idea, and then import the required packages in the pom.xml file;

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.23</version>
        </dependency>

2) Create a com.ioc package in the src directory, create the interface UserDao in the package, and then define a say() method in the interface;

package com.ioc;

public interface UserDao {
    public void say();
}

3) Under the com.ioc package, create the implementation class UserDaoImpl of the UserDao interface. This class needs to override the say() method in the interface;

package com.ioc;

public class UserDaoImpl implements UserDao{
    @Override
    public void say() {
        System.out.println("UserDao say hello world");
    }
}

4) In the resources directory, create the applicationContext.xml file and create a Bean with the id userDao in the configuration file;

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--将指定类配置给Spring,让Spring创建其对象的实例-->
    <bean id="userDao" class="com.ioc.UserDaoImpl"/>
</beans>

Note: Lines 2-5 are Spring's constraint configuration, and line 7 of code indicates creating a Bean instance with the id userDao in the Spring container, where the class attribute is used to specify the class that needs to instantiate the Bean.

5) Under the com.ioc package, create the test class TestIoC and write the main method in the class. In the main method, you need to initialize the Spring container and load the configuration file, then obtain the userDao instance (i.e. Java object) through the Spring container, and finally call the say() method;

package com.ioc;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestIoC {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserDao userDao =(UserDao)applicationContext.getBean("userDao");
        userDao.say();
    }
}

After the program is executed, the console output is as follows

It can be seen that the console has successfully output the output statement in the UserDaoImpl class. But in step 5, we did not use the new keyword to create the implementation class object of the UserDao interface, but obtained the implementation class object through the Spring container. This is the working mechanism of the SpringIoC container.

4.DI

DI (Dependency Injection) dependency injection

The entire process of establishing dependencies between beans in the container is called dependency injection

The function of dependency injection is to dynamically inject the objects it depends on into the Bean component when using the Spring framework to create an object. There are two ways to implement it, one is setter method injection, and the other is constructor release injection. Specifically as follows:

Property setter method injection: refers to the Spring container using the setter method to inject dependent instances. After instantiating a bean by calling the parameterless constructor or parameterless static factory method, calling the bean's setter method, and then calling the bean's setter method, dependency injection can be based on the setter method.

Constructor injection: refers to the Spring container using the constructor method to inject dependent instances. Constructor-based dependency injection is implemented by calling the constructor with parameters. Each parameter represents a dependency.

5. Dependency injection example (taking setter method injection as an example)

1) In the com.ioc package, create the interface UserService and write a method say() in the interface

package com.ioc;

public interface UserService {
    public void say();
}

2) In the com.ioc package, create the implementation class UserServiceImpl of the UserService interface, declare the userDao attribute in the class, and add the setter method of the attribute

package com.ioc;

public class UserServiceImpl implements UserService{
    //声明UserDao属性
    private UserDao userDao;
    //添加UserDao属性的setter方法,用于实现依赖注入
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
    @Override
    public void say() {
    //调用userDao中的say()方法,并执行输出语句
        this.userDao.say();
        System.out.println("UserService say hello world!");
    }
}

3) In the configuration file applicationContext.xml, create a Bean with the id userService. This Bean is used to instantiate the information of the UserServiceImpl class and inject the userDao instance into userService.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="userDao" class="com.ioc.UserDaoImpl"/>
    <bean id="userService" class="com.ioc.UserServiceImpl">
        <!--将id为userDao的Bean实例注入到userService实例中-->
        <property name="userDao" ref="userDao"/>
    </bean>
</beans>

In the above code, <property> is a child element of the <bean> element, which is used to call the setUserDao() method in the Bean instance to complete the assignment of properties, thereby achieving dependency injection. The name attribute represents the corresponding attribute name in the Bean instance, and the ref attribute is used to specify which bean the attribute value refers to.

4) Under the com.ioc package, create the test class TestDI. to test the program

package com.ioc;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestDI {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserService userService =(UserService)applicationContext.getBean("userService");
        userService.say();
    }
}

5) After executing the program, the console input results are as follows

It can be seen from the console output that the Spring container is used to call the say() method in the UserService implementation class through the say() method in the UserDao implementation class, and the results are output. This is how Spring container property setter method is injected, and it is also the most commonly used method in actual development.

Beans in Spring

Bean configuration

Spring can be regarded as a large factory. The function of this factory is to produce and manage beans in the Spring container. If you want to use this factory in the project, the developer needs to configure the Spring configuration file.

The Spring container supports configuration files in two formats, XML and Properties. In actual development, the most commonly used assembly method is the XML format. This configuration method registers and manages dependencies between beans through XML files.

In Spring, the root element of the XML configuration file is <beans>. <beans> contains multiple <bean> sub-elements. Each <bean> sub-element defines a Bean and describes how the Bean is assembled into Spring. in the container.

Common attributes and sub-elements of <bean> elements
attribute or subelement describe
id The unique identifier of the bean. The Spring container configures and manages the bean through this attribute.
name The Spring container can also configure and manage the beans in the container through this attribute. Multiple names can be specified for the beans in the name attribute, and each name is separated by commas, semicolons or spaces.
class This attribute specifies the specific implementation class of the Bean instance, which must be a complete class name (the fully qualified name of the class)
scope Used to specify the scope of Bean capabilities, its attribute values ​​​​are: singleton (single case), prototype (prototype), request, session, global Session, application and websocket. The default value is singleton
property The child elements of the <Bean> element are used to call the setter method in the Bean instance to complete attribute assignment, thereby completing dependency injection. The name attribute of this attribute specifies the corresponding attribute name in the Bean instance, and the ref or value attribute is used to specify the parameter value.
ref Attributes or sub-elements of elements such as <property> can be used to specify a reference to a Bean instance in the Bean factory.
value Attributes or sub-elements of elements such as <property> can be used to directly specify a constant value.
list Dependency injection for encapsulating List or array types
set Dependency injection for encapsulating set type properties
map Dependency injection for encapsulating map type properties
entry

A child element of the <map> element, used to set a key-value pair. Its key attribute specifies the key value of string type, and the ref or value sub-element specifies its value. Its value can also be specified through the value-ref or value attribute.

Bean instantiation

In an object-oriented program, if you want to use an object, you need to instantiate it first. Similarly, in Spring, if you want to use a bean in the container, you also need to instantiate the bean. There are three ways to instantiate a bean, namely constructor instantiation, static factory instantiation and instance factory instantiation. Since the most commonly used method is constructor instantiation, the following will use constructor instantiation as an example to start the description.

Constructor instantiation

Constructor instantiation means that the Spring container instantiates a Bean through the default parameterless constructor method in the Bean corresponding class.

Case presentation

1) Create the Bean1 class under the com.constructor package, and write a sentence in the parameterless constructor method to determine whether the parameterless method has been called.

package com.constructor;

public class Bean1 {
    public Bean1(){
        System.out.println("Bean的构造方法");
    }
}

2) Define a Bean with the id bean1 in the applicationContext.xml configuration file, and specify its corresponding implementation class through the class attribute

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">
       <bean id="bean1" class="com.constructor.Bean1"/>
</beans>

3) In the com.constructor package, create a test class InstanceTest to test whether the constructor can instantiate the Bean.

package com.constructor;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class InstanceTest {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        Bean1 bean1 =(Bean1) applicationContext.getBean("bean1");
        System.out.println(bean1);
    }
}

After executing the program, the results are as follows

 It can be seen that the Spring container has successfully instantiated the Bean and output the result

Bean scope

In the Spring configuration file, the scope of the bean is specified through the scope attribute of the <bean> element. The value of this attribute is as shown in the following table

Bean scope
scope name
singleton(single case) A bean defined using a singleton has only one instance in the Spring container, which means that no matter how many beans reference it, it always points to the same object.
prototyoe(prototype) Each time a prototype-defined bean is obtained through the Spring container, the container will create a new Bean instance.
request In an HTTP request, the container returns the same instance of the bean. for different HTTP requests. A new Bean will be generated for different HTTP requests, and this Bean will only be used in the current
session In another HTTP Session, the container will return the same instance of the bean. A new Bean will be generated for different HTTP requests, and the Bean is only valid within the current HTTP Session.
globalSession In a global HTTP Session, the container returns the same instance of the bean. Valid only in portlet context
application Create an instance for each ServletContext object. Only takes effect in web-related ApplicationContext.
websocket Create an instance for each websocket object. Only takes effect in web-related ApplicationContext.

Among the 7 scopes in the table, singleton and prototype are the two most commonly used. The following uses examples to illustrate the difference between singleton and prototype.

Example

1) Create a class named User under the com package

package com;

public class User {
    private String name;
    private String age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAge() {
        return age;
    }

    public void setAge(String age) {
        this.age = age;
    }

}

2) Define a Bean with the id user in the applicationContext.xml configuration file, then specify its corresponding implementation class through the class attribute, and finally set the scope value to singleton

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="user" class="com.User" scope="singleton"/>
</beans>

3) Create the UserTest class under the com package

package com;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class UserTest {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        User user =(User) applicationContext.getBean("user");
        User user2 =(User) applicationContext.getBean("user");
        System.out.println(user);
        System.out.println(user2);
    }
}

The running results are as follows:

As can be seen from the output results, the two output results are the same, which shows that the Spring container value creates a Scope class example.

Note: If scope="singleton" is not set, the output result is also an instance, because the default scope of the Spring container is singleton.

4) Modify the scope value of the Bean with id user in the applicationContext.xml configuration file in step 2 to prototype, and then test again

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="user" class="com.User" scope="singleton"/>
</beans>

The test program running results are as follows:

It can be found that the Bean instances output twice are not the same, which means that two different Scope instances are created under the prototype scope;

Bean life cycle

The life cycle of a Bean is the entire process from creation to destruction of the Bean;

In the Spring container, the life cycle of each Bean can be divided into the following stages:

1) Instantiation: When the Spring container starts, an instance of the Bean is created based on the configuration file or annotations.

2) Populate properties: Inject the property values ​​or dependencies of the Bean instance into the Bean instance.

3) Before initialization (Initialization): Before the Bean instance is initialized, call the postProcessBeforeInitialization method of BeanPostProcessor to perform customized initialization operations on the Bean instance.

4) Initialization: When the Bean instance is initialized, call the Bean's initialization method, such as the afterPropertiesSet method that implements the InitializingBean interface or the init-method method specified in the configuration file.

5) After initialization (Initialization): After the Bean instance is initialized, call the postProcessAfterInitialization method of BeanPostProcessor to perform customized post-processing operations on the Bean instance.

6) Before destruction (Destruction): Before the Bean instance is destroyed, call the destroy method that implements the DisposableBean interface or the destroy-method method specified in the configuration file.

7) Destruction: When the Bean instance is destroyed, the resources occupied by the Bean instance are released.

The following is a simple Spring Bean life cycle diagram:


In the Spring container, the life cycle of the Bean is managed by the container. We can customize the initialization and destruction operations of the Bean by implementing the BeanPostProcessor, InitializingBean, and DisposableBean interfaces or using the init-method and destroy-method methods in the configuration file.

Bean assembly (injection) method

XML configuration file assembly

By defining the Bean's properties and dependencies in the XML configuration file, the Spring container creates and manages Bean instances based on the information in the configuration file. For example:

   <bean id="userService" class="com.example.UserService">
       <property name="userDao" ref="userDao"/>
   </bean>

   <bean id="userDao" class="com.example.UserDao"/>

In the above example, a Bean named userService is defined, which depends on a Bean named userDao. The Spring container will first create an instance of userDao and then inject it into the attribute of userService.

Annotation assembly

By using annotations in the Bean class or configuration class to define the Bean's properties and dependencies, the Spring container will create and manage Bean instances based on the annotation information. For example:

    @Service   
    public class UserService {
       @Autowired       
       private UserDao userDao;
        //...
    }
    @Repository   
    public class UserDao {
        //...
    }

In the above example, the @Service and @Repository annotations are used to define the beans, and the @Autowired annotation is used to inject dependencies. The Spring container automatically scans annotated classes and creates and manages Bean instances.

Java configuration class assembly

By defining the Bean's properties and dependencies in the Java configuration class, the Spring container creates and manages Bean instances based on the information in the configuration class. For example:

@Configuration   
public class AppConfig {
       @Bean       
       public UserService userService() {
           UserService userService = new UserService();
           userService.setUserDao(userDao());
           return userService;
       }
   
       @Bean       
       public UserDao userDao() {
           return new UserDao();
       }
   }

In the above example, @Configuration and @Bean annotations are used to define beans and dependencies. The Spring container creates and manages Bean instances based on the information in the configuration class.

automatic assembly

The Spring container automatically injects dependencies based on the bean's type or name. For example:

@Service   
public class UserService {
       @Autowired       
       private UserDao userDao;
       // ...
   }
   
@Repository   
public class UserDao {
       // ...
   }

In the above example, the @Autowired annotation is used to automatically inject dependencies. The Spring container will automatically find qualified beans based on type or name and inject them into properties.
In short, in Spring, there are many ways to assemble beans, and you can choose the most appropriate way to create and manage bean instances according to the actual situation.

SpringAOP

        The full name of AOP is Aspect-Oriented Programming, which is aspect-oriented programming (also called aspect-oriented programming). It is a supplement to object-oriented programming (OOP) and has become a relatively mature programming method.
        In traditional business processing code, operations such as transaction processing and logging are usually performed. Although OOP can achieve code reuse through combination or inheritance, if you want to implement a certain function (such as logging), the same code will still be scattered into various methods. In this way, if you want to turn off a function or modify it, you must modify all related methods. This not only increases the workload of developers, but also increases the error rate of the code.
        In order to solve this problem, the idea of ​​AOP was born. AOP adopts a horizontal extraction mechanism to extract duplicate codes scattered in various methods, and then applies these extracted codes to the places that need to be executed when the program is compiled or run. This method of using a horizontal extraction mechanism is obviously impossible to achieve using traditional OOP thinking, because OOP can only achieve vertical reuse of the parent-child relationship. Liran AOP is a new programming idea but it is not a substitute for OOP. It is only an extension and supplement of OOP.

The core concepts of AOP

1) Aspect: An aspect is a modular cross-cutting concern, which includes notifications and pointcuts. Advice defines when and where code is executed, while pointcuts define where code is executed.

For example: In a web application, logging, security control, transaction processing, etc. are common functions across multiple objects. These functions are called aspects and can be implemented through AOP.

2) Joinpoint: A joinpoint is a point where aspects can be inserted during application execution, such as method calls, exception handling, etc.

For example: In a web application, when the user clicks a button, a connection point will be triggered. Aspects can be inserted on this connection point to implement logging, security control and other functions.

3) Pointcut: Pointcut is a set of connection points, used to define which connection points the aspect is executed on.

For example: In a web application, you can define a pointcut to indicate that all service layer methods are pointcuts, and then insert aspects at this pointcut to implement functions such as transaction processing.

4) Advice: Advice is a specific behavior defined in aspects, including pre-notification, post-notification, return notification, exception notification and surrounding notification, etc.

For example: In a web application, you can define a pre-notification to record logs before the service layer method is executed, or you can define an exception notification to send an alert when the service layer method throws an exception.

5) Aspect Ordering: If multiple aspects process the same connection point, then the execution order of the aspects is a matter of aspect priority.

For example: In a web application, multiple aspects can be defined, such as log aspects, security aspects, transaction aspects, etc. These aspects may process the same service layer method. In this case, it is necessary to define the priority of the aspects to determine each aspect. The execution order of aspects.

6) Weaving: Weaving is the process of applying aspects to target objects and creating new proxy objects.

dynamic proxy

Dynamic proxy is a way to generate proxy classes when the program is running. It can dynamically create proxy objects for the target objects, thereby realizing proxy operations on the target objects. There are two main ways of dynamic proxy in Java: JDK dynamic proxy and CGLIB proxy.

JDK dynamic proxy

JDK dynamic proxy is an interface-based proxy method that dynamically creates proxy objects at runtime. JDK dynamic proxy requires the target object to implement one or more interfaces. The proxy object will also implement these interfaces and delegate method calls to the target object.

Sample code:

public interface HelloService {
    void sayHello(String name);
}

public class HelloServiceImpl implements HelloService {
    @Override
    public void sayHello(String name) {
        System.out.println("Hello, " + name);
    }
}

public class HelloServiceProxy implements InvocationHandler {
    private Object target;

    public HelloServiceProxy(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Before method " + method.getName());
        Object result = method.invoke(target, args);
        System.out.println("After method " + method.getName());
        return result;
    }
}

public class Main {
    public static void main(String[] args) {
        HelloService helloService = new HelloServiceImpl();
        HelloServiceProxy proxy = new HelloServiceProxy(helloService);
        HelloService helloServiceProxy = (HelloService) Proxy.newProxyInstance(
                helloService.getClass().getClassLoader(),
                helloService.getClass().getInterfaces(),
                proxy);
        helloServiceProxy.sayHello("World");
    }
}

CGLIBAgent

CGLIB proxy is a class-based proxy method that dynamically creates subclasses of the target class as proxy objects at runtime. The CGLIB proxy does not require the target object to implement the interface. The proxy object is a subclass of the target object and can directly call the target object's methods.

Sample code:

public class HelloService {
    public void sayHello(String name) {
        System.out.println("Hello, " + name);
    }
}

public class HelloServiceInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("Before method " + method.getName());
        Object result = proxy.invokeSuper(obj, args);
        System.out.println("After method " + method.getName());
        return result;
    }
}

public class Main {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(HelloService.class);
        enhancer.setCallback(new HelloServiceInterceptor());
        HelloService helloServiceProxy = (HelloService) enhancer.create();
        helloServiceProxy.sayHello("World");
    }
}

AOP implementation

There are two main ways to implement AOP: proxy-based and bytecode-based enhancement. The proxy-based AOP implementation dynamically creates proxy objects at runtime and weaves notifications into the methods of the target object. The AOP implementation based on bytecode enhancement modifies the bytecode at compile time and weaves the notification into the method of the target object.

Sample code:

public interface HelloService {
    void sayHello(String name);
}

public class HelloServiceImpl implements HelloService {
    @Override
    public void sayHello(String name) {
        System.out.println("Hello, " + name);
    }
}

public class LogAspect {
    public void before() {
        System.out.println("Before method");
    }

    public void after() {
        System.out.println("After method");
    }
}

public class LogAspectJ {
    public void before(JoinPoint joinPoint) {
        System.out.println("Before method " + joinPoint.getSignature().getName());
    }

    public void after(JoinPoint joinPoint) {
        System.out.println("After method " + joinPoint.getSignature().getName());
    }
}

public class HelloServiceProxy implements InvocationHandler {
    private Object target;
    private Object aspect;

    public HelloServiceProxy(Object target, Object aspect) {
        this.target = target;
        this.aspect = aspect;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Method before = aspect.getClass().getMethod("before");
        before.invoke(aspect);
        Object result = method.invoke(target, args);
        Method after = aspect.getClass().getMethod("after");
        after.invoke(aspect);
        return result;
    }
}

public class Main {
    public static void main(String[] args) {
        HelloService helloService = new HelloServiceImpl();
        LogAspect aspect = new LogAspect();
        HelloServiceProxy proxy = new HelloServiceProxy(helloService, aspect);
        HelloService helloServiceProxy = (HelloService) Proxy.newProxyInstance(
                helloService.getClass().getClassLoader(),
                helloService.getClass().getInterfaces(),
                proxy);
        helloServiceProxy.sayHello("World");
    }
}

AspectJ development

 AspectJ is an AOP framework based on the Java language and supports two development methods: XML-based and annotation-based.

The XML-based development method needs to be compiled using the AspectJ compiler, while the annotation-based development method can be compiled directly using the Java compiler.

The <aop:config> element and its sub-elements

element name introduce Attributes
<aop:config> The root element used to define the Spring AOP configuration file
  • proxy-target-class: Specifies whether to use CGLIB proxy. The default is false, which means using JDK dynamic proxy.
  • expose-proxy: Specifies whether to expose the proxy object to the next notification or aspect on the AOP proxy chain. The default is false.
<aop:aspect> Used to define an aspect
  • id: The unique identifier of the aspect.
  • ref: A reference to the aspect’s implementation class.
  • order: the execution order of aspects, the default is 0, the smaller the number, the earlier it is executed.
<aop:pointcut> Used to define a pointcut
  • id: The unique identifier of the pointcut.
  • expression: The expression of the entry point, used to match the method of the target object.
<aop:advisor> Used to define a notifier
  • advice-ref: Reference to the adviser.
  • pointcut-ref: Reference to the pointcut.
<aop:after-returnding> Used to define a return notification
  • method: the name of the notification method.
  • pointcut: expression of pointcut, used to match the method of the target object.
  • returning: the return value parameter name of the notification method.
<aop:before> Used to define a pre-notification
  • method: the name of the notification method.
  • pointcut: expression of pointcut, used to match the method of the target object.
<aop:after> Used to define a final notification
  • method: the name of the notification method.
  • pointcut: expression of pointcut, used to match the method of the target object.
<aop:around> Used to define a surround notification
  • method: the name of the notification method.
  • pointcut: expression of pointcut, used to match the method of the target object.

Detailed introduction to AspectJ annotations

Annotation name introduce
@Aspect Used to define an aspect, it needs to be used together with other annotations, such as @Before, @After and other annotations.
@Pointcut annotation Used to define a pointcut, which can be referenced by other annotations, such as @Before, @After and other annotations.
@Before Used to define a pre-advice, which requires specifying a pointcut expression.
@After Used to define a final advice, which requires specifying a pointcut expression.
@AfterReturning Used to define a return notification, which requires specifying the pointcut expression and return value parameter name.
@Around Used to define a surround advice, which requires specifying a pointcut expression.

Example:

@Aspect
public class LogAspect {
    @Pointcut("execution(* com.example.service.*.*(..))")
    public void servicePointcut() {}

    @Before("servicePointcut()")
    public void before(JoinPoint joinPoint) {
        System.out.println("Before: " + joinPoint.getSignature().getName());
    }

    @AfterReturning(pointcut = "servicePointcut()", returning = "result")
    public void afterReturning(JoinPoint joinPoint, Object result) {
        System.out.println("AfterReturning: " + joinPoint.getSignature().getName() + ", result: " + result);
    }

    @After("servicePointcut()")
    public void after(JoinPoint joinPoint) {
        System.out.println("After: " + joinPoint.getSignature().getName());
    }

    @Around("servicePointcut()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("Around before: " + proceedingJoinPoint.getSignature().getName());
        Object result = proceedingJoinPoint.proceed();
        System.out.println("Around after: " + proceedingJoinPoint.getSignature().getName());
        return result;
    }
}

在上面的示例中,定义了一个LogAspect切面,它包含了@Before、@AfterReturning、@After和@Around等注解,并且使用@Pointcut注解定义了一个切入点。在@Before、@AfterReturning、@After和@Around注解中,都使用了切入点表达式来指定需要拦截的方法。

基于XML的声明式AspecJ

在使用XML方式开发时,需要先定义一个切面类和切入点,然后在XML配置文件中进行配置。例如:

切面类:

public aspect LogAspect {
    private Logger logger = LoggerFactory.getLogger(LogAspect.class);

    pointcut logPointcut(): execution(* com.example.service.*.*(..));

    before(): logPointcut() {
        logger.info("方法执行前记录日志");
    }

    after(): logPointcut() {
        logger.info("方法执行后记录日志");
    }
}

在XML配置文件中配置切面和切入点:

<aop:config>
    <aop:aspect id="logAspect" ref="logAspect">
        <aop:pointcut id="logPointcut" expression="execution(* com.example.service.*.*(..))"/>
        <aop:before pointcut-ref="logPointcut" method="before"/>
        <aop:after pointcut-ref="logPointcut" method="after"/>
    </aop:aspect>
</aop:config>

基于注解的声明式AspecJ

在使用注解方式开发时,只需要在切面类中添加注解即可。例如:

@Aspect
@Component
public class LogAspect {
    private Logger logger = LoggerFactory.getLogger(LogAspect.class);

    @Pointcut("execution(* com.example.service.*.*(..))")
    public void logPointcut() {}

    @Before("logPointcut()")
    public void before() {
        logger.info("方法执行前记录日志");
    }

    @After("logPointcut()")
    public void after() {
        logger.info("方法执行后记录日志");
    }
}

在Spring配置文件中开启AspectJ自动代理:

<aop:aspectj-autoproxy/>

Spring的数据库开发

Spring是一个流行的开源框架,提供了一组强大的数据访问技术,包括Spring JDBC和Spring JdbcTemplate。这些技术可以帮助开发人员更轻松地访问和操作数据库。下面我们来详细介绍一下Spring的数据开发以及Spring JDBC和Spring JdbcTemplate的常用方法。

Spring的数据开发

Spring的数据开发主要包括以下几个方面:

1)数据库连接管理

Spring提供了一个用于管理数据库连接的抽象层,可以让开发人员更方便地管理数据库连接。开发人员可以通过配置文件来指定数据库连接信息,Spring会自动创建和管理数据库连接池。

2)数据库事务管理

Spring提供了一个用于管理事务的抽象层,可以让开发人员更方便地管理数据库事务。开发人员可以通过配置文件来指定事务管理器,Spring会自动处理事务的提交和回滚。

3)数据库访问对象(DAO)

Spring提供了一个用于访问数据库的抽象层,可以让开发人员更方便地访问数据库。开发人员可以通过配置文件来指定数据访问对象,Spring会自动创建数据访问对象并管理其生命周期。

Spring JDBC

Spring JDBC是Spring框架提供的一种简单易用的JDBC封装技术,它可以帮助开发人员更方便地使用JDBC进行数据库操作。Spring JDBC封装了JDBC的一些繁琐的操作,提供了一些简单易用的API,可以让开发人员更快速地进行数据库操作。

Spring JDBC的常用方法包括:

1)获取数据库连接

使用Spring JDBC可以通过调用JdbcTemplate对象的getDataSource方法来获取数据库连接。

2)执行SQL语句

使用Spring JDBC可以通过调用JdbcTemplate对象的execute方法来执行SQL语句。execute方法可以接受一个SQL语句和一个SqlParameterSource对象作为参数,SqlParameterSource对象用于传递SQL语句中的参数。

3)执行查询操作

使用Spring JDBC可以通过调用JdbcTemplate对象的query方法来执行查询操作。query方法可以接受一个SQL语句、一个SqlParameterSource对象和一个RowMapper对象作为参数,RowMapper对象用于将查询结果映射到Java对象中。

4)执行更新操作

使用Spring JDBC可以通过调用JdbcTemplate对象的update方法来执行更新操作。update方法可以接受一个SQL语句和一个SqlParameterSource对象作为参数,SqlParameterSource对象用于传递SQL语句中的参数。

5)执行批量更新操作

使用Spring JDBC可以通过调用JdbcTemplate对象的batchUpdate方法来执行批量更新操作。batchUpdate方法可以接受一个SQL语句和一个SqlParameterSource数组作为参数,SqlParameterSource数组用于传递SQL语句中的多个参数。

下面是一个使用Spring JDBC进行数据库操作的示例代码:

public class UserDaoImpl implements UserDao {

    private JdbcTemplate jdbcTemplate;

    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    public User findById(int id) {
        String sql = "SELECT * FROM user WHERE id = ?";
        SqlParameterSource params = new MapSqlParameterSource("id", id);
        User user = jdbcTemplate.queryForObject(sql, params, new UserRowMapper());
        return user;
    }

    public void save(User user) {
        String sql = "INSERT INTO user (name, age) VALUES (:name, :age)";
        SqlParameterSource params = new BeanPropertySqlParameterSource(user);
        jdbcTemplate.update(sql, params);
    }

    public void update(User user) {
        String sql = "UPDATE user SET name = :name, age = :age WHERE id = :id";
        SqlParameterSource params = new BeanPropertySqlParameterSource(user);
        jdbcTemplate.update(sql, params);
    }

    public void delete(int id) {
        String sql = "DELETE FROM user WHERE id = ?";
        SqlParameterSource params = new MapSqlParameterSource("id", id);
        jdbcTemplate.update(sql, params);
    }

    private static final class UserRowMapper implements RowMapper<User> {
        public User mapRow(ResultSet rs, int rowNum) throws SQLException {
            User user = new User();
            user.setId(rs.getInt("id"));
            user.setName(rs.getString("name"));
            user.setAge(rs.getInt("age"));
            return user;
        }
    }
}

Spring JdbcTemplate

Spring JdbcTemplate是Spring JDBC的一个子项目,它提供了更便捷的JDBC操作方式,并且可以自动处理JDBC中的异常和数据类型转换。Spring JdbcTemplate使用起来比Spring JDBC更加简单,而且性能也更好。

Spring JdbcTemplate的常用方法包括:

1)执行SQL语句

使用Spring JdbcTemplate可以通过调用JdbcTemplate对象的execute方法来执行SQL语句。execute方法可以接受一个SQL语句和一个PreparedStatementCallback对象作为参数,PreparedStatementCallback对象用于处理PreparedStatement对象。

2)执行查询操作

使用Spring JdbcTemplate可以通过调用JdbcTemplate对象的query方法来执行查询操作。query方法可以接受一个SQL语句、一个RowMapper对象和一个可变参数数组作为参数,可变参数数组用于传递SQL语句中的参数。

3)执行更新操作

使用Spring JdbcTemplate可以通过调用JdbcTemplate对象的update方法来执行更新操作。update方法可以接受一个SQL语句和一个可变参数数组作为参数,可变参数数组用于传递SQL语句中的参数。

4)执行批量更新操作

使用Spring JdbcTemplate可以通过调用JdbcTemplate对象的batchUpdate方法来执行批量更新操作。batchUpdate方法可以接受一个SQL语句、一个BatchPreparedStatementSetter对象和一个可变参数数组作为参数,BatchPreparedStatementSetter对象用于设置PreparedStatement对象的参数。

下面是一个使用Spring JdbcTemplate进行数据库操作的示例代码:

public class UserDaoImpl implements UserDao {

    private JdbcTemplate jdbcTemplate;

    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    public User findById(int id) {
        String sql = "SELECT * FROM user WHERE id = ?";
        Object[] params = {id};
        User user = jdbcTemplate.queryForObject(sql, params, new UserRowMapper());
        return user;
    }

    public void save(User user) {
        String sql = "INSERT INTO user (name, age) VALUES (?, ?)";
        Object[] params = {user.getName(), user.getAge()};
        jdbcTemplate.update(sql, params);
    }

    public void update(User user) {
        String sql = "UPDATE user SET name = ?, age = ? WHERE id = ?";
        Object[] params = {user.getName(), user.getAge(), user.getId()};
        jdbcTemplate.update(sql, params);
    }

    public void delete(int id) {
        String sql = "DELETE FROM user WHERE id = ?";
        Object[] params = {id};
        jdbcTemplate.update(sql, params);
    }

    private static final class UserRowMapper implements RowMapper<User> {
        public User mapRow(ResultSet rs, int rowNum) throws SQLException {
            User user = new User();
            user.setId(rs.getInt("id"));
            user.setName(rs.getString("name"));
            user.setAge(rs.getInt("age"));
            return user;
        }
    }
}

Spring的事务管理

Spring的事务管理是Spring框架中的一个重要功能,它可以帮助我们管理应用程序中的事务,确保数据的完整性和一致性。Spring的事务管理支持编程式事务管理和声明式事务管理两种方式,其中声明式事务管理又分为基于XML方式和基于Annotation方式两种。

基于XML方式的声明式事务管理

在基于XML方式的声明式事务管理中,我们需要通过配置XML文件来实现事务管理。首先,我们需要在XML文件中配置TransactionManager和DataSource,然后通过AOP配置来实现事务管理。具体步骤如下:

1)配置TransactionManager和DataSource

<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://localhost:3306/test"/>
    <property name="username" value="root"/>
    <property name="password" value="password"/>
</bean>

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>

2)配置AOP

<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <tx:method name="save"/>
        <tx:method name="update"/>
        <tx:method name="delete"/>
        <tx:method name="find*" read-only="true"/>
    </tx:attributes>
</tx:advice>

<aop:config>
    <aop:pointcut id="userServicePointcut" expression="execution(* com.example.service.UserService.*(..))"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="userServicePointcut"/>
</aop:config>

在上面的代码中,我们通过tx:advice配置了一个名为txAdvice的事务通知,它的transaction-manager属性指定了我们之前配置的TransactionManager。然后,我们通过tx:attributes配置了一些方法的事务属性,比如save、update、delete方法需要进行事务管理,而find*方法不需要。最后,我们通过aop:config配置了一个名为userServicePointcut的切入点,它的expression属性指定了需要进行事务管理的方法所在的类和方法名,然后通过aop:advisor将txAdvice和userServicePointcut绑定在一起。

基于Annotation方式的声明式事务管理

在基于Annotation方式的声明式事务管理中,我们可以使用@Transactional注解来标记需要进行事务管理的方法。具体步骤如下:

1)配置TransactionManager和DataSource(同上)

2)在需要进行事务管理的方法上添加@Transactional注解

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserDao userDao;

    @Override
    @Transactional
    public void save(User user) {
        userDao.save(user);
    }

    @Override
    @Transactional
    public void update(User user) {
        userDao.update(user);
    }

    @Override
    @Transactional
    public void delete(int id) {
        userDao.delete(id);
    }

    @Override
    @Transactional(readOnly = true)
    public User findById(int id) {
        return userDao.findById(id);
    }
}

在上面的代码中,我们在需要进行事务管理的方法上添加了@Transactional注解,其中readOnly属性表示该方法是只读的,不需要进行事务管理。

总之,Spring的事务管理非常重要,可以帮助我们管理应用程序中的事务,确保数据的完整性和一致性。无论是基于XML方式的声明式事务管理还是基于Annotation方式的声明式事务管理,都可以满足我们的需求,具体使用哪种方式取决于个人喜好和项目需求。

Guess you like

Origin blog.csdn.net/qq_61902168/article/details/131008379