Spring学习笔记


优点:

  • Spring 是一个开源的、免费的框架(容器)
  • Spring 是一个轻量级、非入侵式的框架
  • 控制反转(IOC)、面向切面编程(AOP)
  • 支持事务,对框架整合的支持

需要导入的 jar 包:

<!-- 导入这个包就直接把其它的也导进来了,包括 core、context、aop、bean... 的包-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.3.5</version>
</dependency>

一、IOC控制反转

就是把创建对象的操作交给 spring 去做,可以做到解耦,增加程序的可扩展性。

如果不使用 IOC 的话,创建对象是这样创建的:

1. 传统的创建对象方式:

DAO层:

public interface UserDao {
    void getUsers();
}

public class UserDaoImpl implements UserDao {
    public void getUsers() {
        System.out.println("获取用户列表!");
    }
}

public class UserDaoMysqlImpl implements UserDao{
    public void getUsers() {
        System.out.println("获取 mysql 用户列表!");
    }
}

public class UserDaoOracleImpl implements UserDao{
    public void getUsers() {
        System.out.println("获取 oracle 用户列表!");
    }
}

service层:

public interface UserService {
    void getUsers();
}

public class UserServiceImpl implements UserService {
    private UserDao userDao = new UserDaoImpl();
    
    public void getUsers() {
        userDao.getUsers();
    }
}

Test类:

public static void main(String[] args) {
    UserService userService = new UserServiceImpl();
    userService.getUsers();
}

这样的话,调用的是 UserDaoImpl 实现类的方法,如果要调用 UserDaoMysqlImpl 或 UserDaoOracleImpl 的方法,就需要在 service 层修改代码,把 private UserDao userDao = new UserDaoImpl() 改成 private UserDao userDao = new UserDaoMysqlImpl() ,这样造成各个模块之间极大的耦合,而且如果后期修改需求的话,还得修改源代码,灵活性极差,所以就需要 spring 的控制反转,通过配置文件创建对象,这样主动权就交给了用户手上,而不是程序员手上

2. IOC 创建对象:
2.1 无参构造创建:

applicationContext.xml

<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="studentDaoImpl" class="com.nari.dao.StudentDaoImpl"/>
    <bean id="teacherDaoImpl" class="com.nari.dao.TeacherDaoImpl"/>

    <bean id="studentServiceImpl" class="com.nari.service.StudentServiceImpl">
        <property name="studentDao" ref="studentDaoImpl"/>
    </bean>

</beans>

DAO 层:

public interface StudentDao {
    void getStudent();
}

public class StudentDaoImpl implements StudentDao{
    @Override
    public void getStudent() {
        System.out.println("获取学生!");
    }
}

public class TeacherDaoImpl implements StudentDao{
    @Override
    public void getStudent() {
        System.out.println("获取老师!");
    }
}

Service 层:

public interface StudentService {
    void getStudent();
}

public class StudentServiceImpl implements StudentService{
    @Setter
    private StudentDao studentDao;
    @Override
    public void getStudent() {
        studentDao.getStudent();
    }
}

Test类:

@Test
public void getUser(){
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
    StudentServiceImpl studentServiceImpl = (StudentServiceImpl) applicationContext.getBean("studentServiceImpl");
    studentServiceImpl.getStudent();
}

这样就通过配置文件创建了对象,当程序一运行,spring 就自动帮我们创建好了对象

2.2 有参构造创建:
<!-- 有参构造创建对象的三种方式 -->
<!-- 使用下标的方式 -->
<constructor-arg index="0" ref="studentDaoImpl"/>
<!-- 使用属性名称 -->
<constructor-arg name="studentDao" ref="studentDaoImpl"/>
<!-- 使用属性类型 -->
<constructor-arg type="com.nari.dao.StudentDao" ref="studentDaoImpl"/>

推荐使用属性名称的方式进行创建

二、spring 配置文件详解:

alias:别名(不是给类起全限定名,而是给 id 起)

<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="studentDaoImpl" class="com.nari.dao.StudentDaoImpl"/>

    <bean id="studentServiceImpl" class="com.nari.service.StudentServiceImpl">
        <property name="studentDao" ref="studentDaoImpl"/>
    </bean>
    
	<alias name="studentDaoImpl" alias="student"/>
</beans>

import:导入配置文件

主要是多人协作时用的,比如有一个主配置文件 applicationContent.xml,多个不同开发人员配置的文件 beans.xml,就可以在主配置文件中导入从配置文件

<import resource="beans1.xml"/>
<import resource="beans2.xml"/>
<import resource="beans3.xml"/>

spring 会自动对配置文件进行整合,如果有相同的配置,spring 会自动选择其中一个

bean:创建对象

<bean id="studentDaoImpl" class="com.nari.dao.StudentDaoImpl" name="u1 u2,u3;u4"/>

name 属性也是别名,比 alias 标签强大多了,推荐使用,而且可以起多个别名,能想到的分隔符都可以处理

三、依赖注入

就是创建对象的方式,本质其实就是 set 注入,如果没有 set 方法,是无法进行注入的

依赖注入:

  • 依赖:bean 对象的创建依赖于 spring 容器
  • 注入:bean 对象中的所有属性,由容器进行注入
注入方式目前有三种
3.1 构造器注入

有参构造注入

无参构造注入

3.2 其它方式注入

p 命名空间注入:p 就是 properties 属性注入

<bean id="address" class="com.nari.pojo.Address" p:address="山西"/>

c 命名空间注入:c 就是 constructor 构造器注入

<bean id="address" class="com.nari.pojo.Address" c:address="运城"/>

注意点:

  • 使用之前需导入相关约束

  • xmlns:p="http://www.springframework.org/schema/p"
    xmlns:c="http://www.springframework.org/schema/c"
    
3.3 set 注入(重要)

pojo 实体类:

@Data
public class Student {
    private String name;
    private Address address;
    private String[] books;
    private List<String> hobbys;
    private Map<String, String> card;
    private Set<String> games;
    private String wife;
    private Properties info;
}

beans.xml:

<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="address" class="com.nari.pojo.Address">
        <property name="address" value="山西"/>
    </bean>

    <bean id="student" class="com.nari.pojo.Student">
        <!-- 1. 基本类型注入 -->
        <property name="name" value="张三"/>

        <!-- 2. 引用类型注入 -->
        <property name="address" ref="address"/>

        <!-- 3. 数组注入 -->
        <property name="books">
            <array>
                <value>语文</value>
                <value>数学</value>
                <value>政治</value>
                <value>英语</value>
            </array>
        </property>

        <!-- 4. list 集合注入 -->
        <property name="hobbys">
            <list>
                <value>看书</value>
                <value>学习</value>
                <value>谈恋爱</value>
            </list>
        </property>

        <!-- 5. map 类型注入 -->
        <property name="card">
            <map key-type="java.lang.String" value-type="java.lang.String">
                <entry key="身份证" value="14273100114580"/>
                <entry key="学生证" value="20210678"/>
                <entry key="银行卡" value="12345812354531351352"/>
            </map>
        </property>
        <!-- 6. set 类型注入 -->
        <property name="games">
            <set>
                <value>LOL</value>
                <value>BOB</value>
                <value>COC</value>
            </set>
        </property>
        
        <!-- 7. NULL 类型注入 -->
        <property name="wife">
            <null/>
        </property>

        <!-- 8. properties 类型注入 -->
        <property name="info">
            <props>
                <prop key="url">java.lang.String</prop>
                <prop key="driver">jdbc://localhost:5236/DAMENG</prop>
                <prop key="username">root</prop>
                <prop key="password">ruirui</prop>
            </props>
        </property>
    </bean>
</beans>

四、Bean 的作用域

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PLK2i8EA-1622863598370)(/home/yijie/.config/Typora/typora-user-images/image-20210418231354846.png)]

bean 标签中的 scope 属性,主要分为四个值:

  • singleton:单例模式(全局共享)
  • prototype:原型模式(非全局共享)
  • request
  • session

注意点:

  • 单例模式,两个创建的对象的 hashcode 是一样的,单线程时使用
  • 原型模式,两个创建的对象是不一样的,多线程时使用

五、自动装配 Bean

  • 自动装配是 spring 满足 bean 依赖的一种方式
  • spring 会在上下文中自动寻找,并自动给 bean 装配属性

在 spring 中,有三种装配的方式:

  • 在 xml 中显式的配置:就是上面的注入
  • 在 java 代码中显式装配
  • 隐式的自动装配 bean「重要」
装配方式一:xml
<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="cat" class="com.nari.spring.pojo.Cat"/>
    <bean id="dog" class="com.nari.spring.pojo.Dog"/>

    <bean id="person" class="com.nari.spring.pojo.Person" autowire="byName">
        <property name="name" value="张三"/>
    </bean>
</beans>

通过 bean 标签的 autowire 属性,此属性有五个值:

1、byName:通过 bean 标签的 id 去匹配

2、byType:通过 bean 标签的 class 属性匹配

3、constructor:通过构造器匹配

4、default:通过默认的方式匹配,默认方式为 no

5、no:不进行自动装配

注意点:

  • byName 方式必须保证 bean 的 id 唯一,id 不可省略
  • byType 方式必须保证 bean 的 class 唯一,且 id 可以省略
装配方式二:注解
1、@Autowired
public class Person {
    @Autowired
    private Cat cat;
    @Autowired
    private Dog dog;
    private String name;

}
<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">
	
    <!-- 开启注解支持 -->
    <context:annotation-config/>

    <bean id="cat" class="com.nari.spring.pojo.Cat"/>
    <bean id="dog" class="com.nari.spring.pojo.Dog"/>
    <bean id="person" class="com.nari.spring.pojo.Person"/>
</beans>
public class SpringTest {
    @Test
    public void DemoTest(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        Person person = applicationContext.getBean("person",Person.class);
        person.getDog().shout();
        person.getCat().shout();
    }
}

注意事项:

  • 需要导入相关约束:导入 context 约束

  • 需要开启注解支持

  • @Autowired 注解有且仅有一个属性 required

    public @interface Autowired {
        boolean required() default true;
    }
    

    ​ 如果 required 设置为 false,表示这个对象可以为 null

    public class Person {
        @Autowired(required = false)
        private Cat cat;
    }
    
  • @Autowired 注解可以放到 set 方法上,和放在属性上的效果是一样的

    @Autowired
    public void setCat(Cat cat) {
        this.cat = cat;
    }
    
  • 如果使用 @Autowired 注解,可以省略 set 方法,但是 get 方法不可省略

  • @Autowired 默认是通过 byName 实现自动装配,如果名字匹配不上,就通过 byType 匹配

2、@Qualifier

如果 @Autowired 的自动装配环境比较复杂,有多个类型相同且 id 不同的 bean 时,可以搭配使用 @Qualifier 注解,去指定一个 bean 去装配

<context:annotation-config/>
<bean id="cat111" class="com.nari.spring.pojo.Cat"/>
<bean id="cat11" class="com.nari.spring.pojo.Cat"/>

<bean id="dog111" class="com.nari.spring.pojo.Dog"/>
<bean id="dog11" class="com.nari.spring.pojo.Dog"/>
public class Person {
    @Autowired
    @Qualifier(value = "cat11")
    private Cat cat;
    @Autowired
    @Qualifier(value = "dog111")
    private Dog dog;
}
3、@Resource

此注解是 java 里自带的,不属于 spring,但是功能和 @Autowired 一样的,也是用于实现自动装配,它相当于是 @Autowired 和 @Qualifier 的集合体

public class Person {
    @Autowired
    @Qualifier(value = "cat11")
    private Cat cat;
    
    @Resource(name = "dog111")
    private Dog dog;
}

该注解也是先通过属性名匹配,匹配不到的话在通过类型匹配

4、@Nullable

该注解放到属性上,表示该属性可以为 null 值

public Person(@Nullable String name) {
    this.name = name;
}

六、使用注解开发

在 spring4 之后,要使用注解开发,要先导入 aop 的包

其次导入 context 约束,并开启注解支持,以及注解扫描

<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">
	
    <!-- 开启注解扫描 -->
    <context:component-scan base-package="com.nari"/>
    <context:annotation-config/>
</beans>
bean注入

相当于:

<bean id="user" class="com.nari.pojo.User">
@Component
public class User {
    public String name;
}
属性注入

相当于:

<property name="name" value="张三"/>
@Component
public class User {
    @Value(value = "张三")
    public String name;
}

注意:

  • 被注入的属性需是 public 的,否则获取不到属性
  • 使用注解,可以省略属性的 get、set 方法,如果用 xml 配置的话,必须有 set 方法
  • 使用注解需要如果不指定 bean 名称,默认是首字母小写的类名,也可自定义 bean 名
@Component 的衍生注解

@Repository、@Service、@Controller

分别对应 dao层、service层、controller层,其效果等同于 @Component,一个意思

作用域
@Component("user")
@Scope(value = "singleton")
public class User {
    @Value(value = "张三")
    public String name;
}
xml 与注解的区别
  • xml 更加万能,适用于各种场景,维护简单
  • 注解不是自己的类,使用不了,维护复杂
  • 最好的办法是:xml 用于管理 bean,注解用于属性的注入

七、基于 java 替代 xml 配置:AppConfig

完全抛弃 xml 的配置,使用 java 代码代替,不用创建 xml 配置文件

1、创建 java 配置类

@Configuration
@ComponentScan(value = "com.nari.pojo")
public class UserConfig {
    @Bean
    public User getUser(){
        return new User();
    }
}
  • @Configuration 表示该类为一个 java 配置类
  • @ComponentScan 扫描哪个包或类的注解
  • 新建一个 pojo 的返回值类型的方法

2、创建 pojo 类

@Data
@Component
public class User {
    @Value(value = "123123")
    private String name;
}

3、进行测试

public class UserTest {
    @Test
    public void UserDemoTest(){
        ApplicationContext context = new AnnotationConfigApplicationContext(UserConfig.class);
        User getUser = context.getBean("getUser", User.class);
        System.out.println(getUser.getName());
    }
}
  • 需要创建 AnnotationConfigApplicationContext 对象,参数为 java 配置类
  • getBean 是通过配置类的方法名称得到 pojo 对象

八、代理模式

1、静态代理

好处:

  • 可以使真实角色的操作更加纯粹,不用去关注一些公共的业务
  • 公共业务交给了代理角色,实现了业务的分工
  • 公共业务在发生扩展的时候,方便集中管理

坏处:

  • 一个真实的角色就需要创建一个代理角色,代码量会变多
代码案例

UserService:

public interface UserService {
    public void add();
    public void delete();
}

UserServiceImpl: 真实角色

public class UserServiceImpl implements UserService{
    @Override
    public void add() {
        System.out.println("调用了 add 方法");
    }

    @Override
    public void delete() {
        System.out.println("调用了 delete 方法");
    }
}

UserProxy:

public class UserProxy implements UserService{

    private UserServiceImpl userService;

    public void setUserService(UserServiceImpl userService) {
        this.userService = userService;
    }

    @Override
    public void add() {
        log("add");
        userService.add();
    }

    @Override
    public void delete() {
        log("delete");
        userService.delete();
    }

    public void log(String msg){
        System.out.println("[Debug] 此处增加了" + msg + "日志!!");
    }
}

Test:

public class ClientDemo {
    @Test
    public void proxyTest(){
        //代理之前
        UserServiceImpl userService = new UserServiceImpl();
        userService.add();
        userService.delete();
		
        //代理之后
        UserProxy userProxy = new UserProxy();
        userProxy.setUserService(userService);
        userProxy.add();
        userProxy.delete();
    }
}
代码详解
  • UserService 是一个抽象接口,限制了实现功能的具体方法
  • UserServiceImpl 是接口的实现类,实现了功能的具体细节
  • UserProxy 是代理类,因为要在不改变原有代码的基础上,增加新的功能,所以还是需要实现接口,所以新的功能的具体实现细节,可以定义在代理类中,然后在原有功能的重写方法里,加上新的功能方法,然后在调用原有实现类的方法,以追加新的功能
2、动态代理
代码案例

User

// 公共接口类
public interface User {
    void add();
    void query();
}

UserImpl

// 真实的角色
public class UserImpl implements User{
    @Override
    public void add() {
        System.out.println("增加了一个用户...");
    }

    @Override
    public void query() {
        System.out.println("查询所有用户...");
    }
}

ProxyInvocationHandler

// 代理类
public class ProxyClassDemo implements InvocationHandler {
    private Object target;

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

//    生成一个代理类
    Object getProxy(){
        return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }

//    执行真实角色的方法,并返回执行结果
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = method.invoke(target, args);
        other(method.getName());
        return result;
    }

//    添加日志功能
    public void other(String methodName){
        System.out.println("执行了 " + methodName + " 方法...");
    }
}

Test

public class ClientDemo {
    public static void main(String[] args) {
//        new 一个真实角色
        Host host = new Host();
//        生成代理类
        ProxyClassDemo proxyClassDemo = new ProxyClassDemo();
//		  传入真实角色的类对象
        proxyClassDemo.setUser(host);
//        强转为公共接口的类型
        Rent proxy = (Rent) proxyClassDemo.getProxy();
//        调用真实对象的方法
        proxy.money();
        proxy.rent();
    }
}
代码详解
  • 动态代理主要分为两大类:

    • 基于接口的动态代理

      • JDK 动态代理 [ 在这里使用 ]
    • 基于类的动态代理

      • cglib
    • 基于 java 字节码实现:

      • javasist
  • 动态代理主要需要用到两个类:

    • Proxy:生成代理类
    • InvocationHandler:调用处理程序
  • ProxyInvocationHandler 类的方法详解:

    • getProxy():生成代理类,通过 Proxy.newProxyInstance() 静态方法动态获得代理类
      • Proxy.newProxyInstance() :
        • 参数一:当前类的类加载器,通过 this.getClass().getClassLoader() 来获取
        • 参数二:公共接口,通过真实角色的 getClass() 的 getInterfaces() 来获取
        • 参数三:InvocationHandler,该类本身,因为该类实现了 InvocationHandler 接口
    • invoke():调用真实角色的类方法,关返回执行结果
      • 直接调用 method.invoke() 方法,并返回执行结果即可
        • invoke():
          • 参数一:公共接口类
          • 参数二:args

九、AOP [ 重要 ]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UDmlsoTc-1622863598372)(/home/yijie/.config/Typora/typora-user-images/image-20210516224926804.png)]

在 spring 中实现 AOP 的三种方式

切面类:就是要实现扩展方法的类

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lHOfcegG-1622863598375)(/home/yijie/.config/Typora/typora-user-images/image-20210516225450025.png)]

  • 不管以下哪三种方式,都需要开启 aop 的支持

    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
            https://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/aop
            https://www.springframework.org/schema/aop/spring-aop.xsd">
    </beans>
    
1.1 基于 spring api 实现 aop

applicationContext.xml:

<bean id="userService" class="com.nari.demo1.service.UserServiceImpl"/>
    <bean class="com.nari.demo1.log.BeforeLog" id="beforeLog"/>
    <bean class="com.nari.demo1.log.AfterLog" id="afterLog"/>

    <aop:config>
		<!-- 设置切入点-->
        <aop:pointcut id="pointcut" expression="execution(* com.nari.demo1.service.UserServiceImpl.*(..))"/>

        <aop:advisor advice-ref="beforeLog" pointcut-ref="pointcut"/>
        <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
    </aop:config>

AfterLog:实现 AfterAdvice 或 AfterReturningAdvice 接口,区别就是一个有方法执行后的返回值

public class AfterLog implements AfterReturningAdvice {
    /**
     *
     * @param returnValue   方法的执行结果
     * @param method        要执行的真实类的方法
     * @param args          方法参数
     * @param target        公共接口
     * @throws Throwable
     */
    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println(target.getClass().getName() + " 执行了 " + method.getName() + " 方法,返回结果:" + returnValue);
        for (int i = 0; i < 60; i++) {
            System.out.print("=");
        }
        System.out.println();
    }
}

BeforeLog:

public class BeforeLog implements MethodBeforeAdvice {
    /**
     *
     * @param method    要执行的真实类的方法
     * @param args      方法的参数
     * @param target    公共接口
     * @throws Throwable
     */
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println(target.getClass().getName() + " 类执行了 " + method.getName() + " 方法");
    }
}

注意

  • 不管是 AfterAdvice、AfterReturningAdvice 或 MethodBeforeAdvice 或 BeforeAdvice,都是继承了 Advice 类

UserServiceImpl:

public class UserServiceImpl implements UserService{
    @Override
    public void add() {
        System.out.println("增加了一个用户...");
    }

    @Override
    public void delete() {
        System.out.println("删除了一个用户...");
    }
}

Test:

public class AOPDemo1 {
    @Test
    public void Demo1Test(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserService userService = applicationContext.getBean("userService", UserService.class);
        userService.add();
        userService.delete();
        userService.select();
    }
}
1.2 通过自定义类实现 aop

applicationContext.xml:

<!-- 注册 bean -->
<bean id="addLog" class="com.nari.demo2.log.AddLog"/>
<bean id="bookService" class="com.nari.demo2.service.BookServiceImpl"/>

<!-- 配置 aop -->
<aop:config>
    <!-- 配置切面,ref 是自定义的切面类 -->
    <aop:aspect ref="addLog">
        <!-- 设置切入点 -->
        <aop:pointcut id="pointcut" expression="execution(* com.nari.demo2.service.BookServiceImpl.*(..))"/>
        <!-- 配置切入方法 -->
        <aop:before method="beforeLog" pointcut-ref="pointcut"/>
        <aop:after method="afterLog" pointcut-ref="pointcut"/>
    </aop:aspect>
</aop:config>

AddLog:

public class AddLog {
    public void beforeLog(){
        System.out.println("在方法执行之前添加了一段日志...");
    }

    public void afterLog(){
        System.out.println("在方法执行之后添加了一段日志...");
    }
}

BookServiceImpl:

public class BookServiceImpl implements BookService{
    @Override
    public void addBook() {
        System.out.println("增加了一本图书...");
    }

    @Override
    public void deleteBook() {
        System.out.println("删除了一本图书...");
    }
}

Test:

public class AOPDemo2 {
    @Test
    public void Demo2Test(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext2.xml");
        BookService bookService = applicationContext.getBean("bookService", BookService.class);
        bookService.addBook();
        bookService.deleteBook();
    }
}
1.3 通过注解方式实现 aop

applicationContext.xml:

<!--    开启注解支持-->
<aop:aspectj-autoproxy/>
<context:component-scan base-package="com.nari.demo2"/>
<context:annotation-config/>

annotationPointCut:

@Aspect
@Component
public class AnnotationPointCut {
    @Before("execution(* com.nari.demo2.service.BookServiceImpl.*(..))")
    public void beforeLog(){
        System.out.println("方式三:方法执行前...");
    }

    @After("execution( * com.nari.demo2.service.BookServiceImpl.*(..))")
    public void afterLog(){
        System.out.println("方式三:方法执行后...");
    }

    @Around("execution(* com.nari.demo2.service.BookServiceImpl.*(..))")
    public void aroundLog(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("方式三:环绕前...");
        joinPoint.proceed();
        System.out.println("方式三:环绕后...");
        System.out.println(joinPoint.getKind());
        Signature signature = joinPoint.getSignature();
        System.out.println(signature.getName());
    }
}
  • joinPoint.proceed() 执行具体方法

Test:

public class AOPDemo3 {
    @Test
    public void demo3Test(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext3.xml");
        BookService bookService = applicationContext.getBean("bookService", BookService.class);
        bookService.deleteBook();
    }
}

十、整合 mybatis

  • 先导入相关 jar 包
  • 编写 pojo 实体类
  • 编写接口
  • 编写 mapper 配置文件
  • 编写 spring 配置文件
  • 编写接口实现类,在实现类中使用 sqlSession 调用方法

需要导入的依赖包:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.3.5</version>
</dependency>

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.9</version>
</dependency>

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.12</version>
</dependency>

<!-- aop 相关依赖 jar 包 -->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjrt</artifactId>
    <version>1.9.1</version>
</dependency>

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.8.6</version>
</dependency>

<!-- 整合 mybatis 相关依赖包 -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-spring</artifactId>
    <version>2.0.6</version>
</dependency>

<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.5</version>
</dependency>

<dependency>
    <groupId>org.mariadb.jdbc</groupId>
    <artifactId>mariadb-java-client</artifactId>
    <version>2.7.2</version>
</dependency>

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.3.6</version>
</dependency>

spring-usermapper.xml:

<!-- dataSource -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="url" value="jdbc:mariadb://localhost:3306/mybatis?characterEncoding=UTF-8&amp;useUnicode=true"/>
    <property name="driverClassName" value="org.mariadb.jdbc.Driver"/>
    <property name="username" value="root"/>
    <property name="password" value="ruirui"/>
</bean>

<!-- sqlSessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>
    <property name="configLocation" value="classpath:mybatis-config.xml"/>
    <!-- 映射 Mapper 配置文件 -->
    <property name="mapperLocations" value="classpath:com/nari/mapper/UserMapper.xml"/>
</bean>

<!-- sqlSession -->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
    <constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>

<!-- 注册 userMapper 接口实现类 bean,将 sqlSession 注入进去 -->
<bean id="userMapper" class="com.nari.mapper.UserMapperImpl">
    <property name="sqlSession" ref="sqlSession"/>
</bean>

userMapperImpl:

@Setter
public class UserMapperImpl implements UserMapper{

    private SqlSessionTemplate sqlSession;
    @Override
    public List<User> selectUser() {
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        return mapper.selectUser();
    }
}

Test:

public class MybatisDemo1 {
    @Test
    public void test1(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-usermapper.xml");
        UserMapper userMapper = applicationContext.getBean("userMapper", UserMapper.class);
        for (User user : userMapper.selectUser()) {
            System.out.println(user);
        }
    }
}

十一、事务

特点:要么都成功,要么都失败,确保数据的完整性和一致性

事务的ACID原则:

  • 原子性
    • 事务的最小单位,无法在进行分割,事务中的操作要么都发生,要么都不发生
  • 一致性
    • 事务提交前后的数据的完整性必须保持一致,即事务完成后,符合逻辑运算
  • 隔离性
    • 保证多个业务操作同一个资源的时候,是隔离的,A事务的操作不影响B事务的操作
  • 持久性
    • 事务一旦提交,无论发生什么情况,数据的结果不会被影响,被持久化到存储器中
1、注解方式整合 mybatis
1.1 开启注解支持以及注解扫描
<context:component-scan base-package="com.narii"/>
<context:annotation-config/>
1.2 使用注解
@Data
@Component("userMapper")
public class UserMapperImpl implements UserMapper {
    @Autowired
    @Qualifier("sqlSession")
    private SqlSessionTemplate sqlSession;

    @Override
    public List<UserPojo> queryUser() { ... }
2、通过切面方式添加事务
<!-- 开启声明式事务 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>

<!-- 使用 spring 整合事务 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <!-- 配置事务的传播特性 -->
    <tx:attributes>
        <!-- 给所有方法添加上事务 -->
        <!-- 事务的传播特性有很多种,REQUIRED 是默认的,即如果没有事务就创建一个事务 -->
        <tx:method name="*" propagation="REQUIRED"/>
    </tx:attributes>
</tx:advice>

<!-- 配置 aop -->
<aop:config>
    <aop:pointcut id="txPointCut" expression="execution(* com.narii.mapper.*.*(..))"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
</aop:config>
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

很简单_

点个赞在走呗 ~~~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值