Spring介绍

Spring:分层的javaSE/EE应用full-stack轻量级开源框架

一、底层是核心容器

1、Beans

2、Core

3、Context

4、SpringEI表达式

二、中间层技术

1、AOP(规范、思想)

2、Aspects(实现方案)

三、应用层技术

1、数据访问与数据集成

2、web集成

3、web实现

四、基于Test测试

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VSd4Oyts-1656900398010)(C:\Users\StarrySea\Desktop\MarkDown学习\图片\spring1.jpg)]

IoC:【控制反转】,Spring反向控制应用程序所需要使用的外部资源

Spring控制的资源全部放在Spring容器中,该容器称为IoC容器

以前主控权在类手中,我们需要取new获取,现在主控权在spring手中,由spring提供资源

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-R3txAwsB-1656900398011)(C:\Users\StarrySea\Desktop\MarkDown学习\图片\spring2.jpg)]

IoC入门案例步骤:

1、pom文件中导入spring坐标(5.1.9.release)

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.1.19.RELEASE</version>
    </dependency>
</dependencies>

2、编写业务层接口和实现类,作为资源

service层的接口
package com.lcl.service;

public interface UserService {
    public void save();
}
service实现类
package com.lcl.service.impl;

import com.lcl.service.UserService;

public class UserServiceImpl implements UserService {
    public void save() {
        System.out.println("user service running...");
    }
}

3、建立spring配置文件

4、配置所需资源(service层的实现类)做为spring控制的资源

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">

    <!--创建spring控制的资源-->
    <bean id="userService" class="com.lcl.service.impl.UserServiceImpl"/>

</beans>

5、表现层(App)通过spring获取资源(获取service层的实现类,创建对象,再调用方法)

import com.lcl.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class UserApp {
    public static void main(String[] args) {
        //2、加载配置文件
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");

        //3、获取资源
        UserService userService = (UserService)ctx.getBean("userService");

        userService.save();
    }
}

bean标签,归属于beans标签,作用:定义spring中的资源,受此标签定义的资源将收到spring控制

<bean id="userService" name="userService1,userService2" class="com.lcl.service.impl.UserServiceImpl"/>

属性:

id:bean的名称,将通过id来获取bean资源,一般为接口名(第一个字母小写)

class:bean的类型,也就是对应资源的实现类

name:它是bean的名称,将通过name来获取bean资源,用于多人配合是给bean起别名。也就说与id一样的功能,可以起很多名字

<bean id="userService3" scope="prototype" class="com.lcl.service.impl.UserServiceImpl"/>

scope:定义bean的作用范围。用于控制bean创建后的对象是否是单例

取值:

1、singleton:设定创建出的对象保存在spring容器中,是一个单例的对象,就是创建出来的对象是同一个对象(加载配置文件时就创建)

2、prototype:设定创建出的对象保存在spring容器中,是一个非单例的对象,就是创建出来的对象不是同一个对象(创建对象时才创建)

3、request、session、application、websocket:设定创建出的对象放置在web容器对应的位置

bean的生命周期:

属性:init-method、destroy-method

要在对应的类中定义方法init、destroy

<bean id="userService3" scope="singleton" init-method="init" destroy-method="destroy" class="com.lcl.service.impl.UserServiceImpl"/>

当scope为singleton,也就是单例时,int只执行一次,而非单例的,则是创建一个对象就执行一次init。单例时,关闭容器就执行destroy一次,非单例不执行,因为这时的对象销毁由垃圾回收机制gc()控制。

bean对象创建方式

属性:factory-bean、factory-method

作用:定义bean对象创建方式,使用实例工厂的形式创建bean,兼容早期遗留系统的升级工作

取值:工厂bean中用于获取对象的实例方法名

注意事项:1、使用实例工厂创建bean首先需要将实例工厂配置bean,交由spring进行管理。2、factory-bean是实例工厂的beanId

 <!--静态工厂创建bean-->
<!-- <bean id="userService4"  class="com.lcl.service.UserServiceFactory" factory-method="getService"/>-->

 <!--实例工厂对应的bean-->
 <bean id="factoryBean"  class="com.lcl.service.UserServiceFactory2" />
 <!--实例工厂创建bean,依赖工厂对象对应的bean-->
 <bean id="userService5" factory-bean="factoryBean" factory-method="getService"/>

DI:依赖注入,应用程序运行依赖的资源由spring为其提供,资源进入应用程序的方式称为注入(与IoC是同时发生,只是站的角度不同,在spring上是IoC,在应用程序上是DI,一个提供资源,一个获取资源)

两种方式

1、set注入(主流方式)

使用property标签,归属于bean标签,作用是使用set方法的形式为bean提供资源

基本属性:

name:对应bean中的属性名,要求该属性必须提供可访问的set方法(严格规范此名称是set方法对应名称)(也就是在一个类中,把资源定义为变量,写个set方法,和平常那个get/set方法一样,只需要set方法)

value:设定非引用类型属性对应的值,不能与ref同时使用

ref:设定引用类型属性对应bean的id值,不能与value同时使用

<!--userService自己就是一个bean资源,被spring控制,其内自己用set注入获取userDao这个资源-->
<bean id="userService" class="com.lcl.service.impl.UserServiceImpl">
    <!--3、将要注入的引用类型的变量,通过property注入,name对应set方法那个方法名中去除set部分,ref对应下面步骤2中注入bean 的id-->
    <property name="userDao" ref="userDao"/>

    <!--非引用类型,不用声明bean,直接写property-->
    <property name="num" value="666"/>
</bean>

<!--2、将要注入的资源声明为bean-->
<bean id="userDao" class="com.lcl.dao.impl.UserDaoImpl"/>

2、构造器注入(了解)

使用constructor-arg标签,归属于bean标签,作用是使用构造方法的形式为bean提供资源,兼容早期遗留系统的升级工作(也就是构建一个有参构造)

基本属性:

name:对应bean中构造方法所携带的【参数名】

value:设定非引用类型构造方法参数对应的值,不能与ref同时使用

ref:设定引用类型属性对应bean的id值,不能与value同时使用

type:设定构造方法【参数的类型】,用于按类型匹配参数或进行类型校验

index:设定构造方法【参数的位置】,用于按位置匹配参数,参数index值从0开始计数(没吊用感觉,还不如用name)

注意:一个bean可以有多个constructor-arg标签

    <bean id="userService" class="com.lcl.service.impl.UserServiceImpl">
       <constructor-arg name="userDao" ref="userDao"/>
        <constructor-arg name="num" value="666"/>
    </bean>

    <bean id="userDao" class="com.lcl.dao.impl.UserDaoImpl">
        <!--如果不用name,值就不能对应相应变量,可以用index-->
        <constructor-arg index="0" value="123456"/>
        <constructor-arg index="1" value="lcl"/>
    </bean>

【集合数据类型】的注入

五个标签:arry、list、set、map、props。都归属于property标签或constructor-arg标签

此处也是set注入,主要看怎么给集合数据注入,添加值

<!--集合数据类型的注入-->
<bean id="userService" class="com.lcl.service.impl.UserServiceImpl">
    <property name="userDao" ref="userDao"/>
    <property name="bookDao" ref="bookDao"/>
</bean>

<bean id="userDao" class="com.lcl.dao.impl.UserDaoImpl">
    <constructor-arg index="0" value="123456"/>
    <constructor-arg index="1" value="lcl"/>
</bean>

<bean id="bookDao" class="com.lcl.dao.impl.BookDaoImpl">
    <property name="arrayList">
        <list>
            <value>lcl</value>
            <value>xwh</value>
        </list>
    </property>
    <property name="properties">
        <props>
            <prop key="name">lcl</prop>
            <prop key="value">666666</prop>
        </props>
    </property>
    <property name="arr">
        <array>
            <value>3</value>
            <value>6</value>
        </array>
    </property>
    <property name="hashSet">
        <set>
            <value>lcl</value>
            <value>xwh</value>
        </set>
    </property>
    <property name="hashMap">
        <map>
            <entry key="name" value="lcl"/>
            <entry key="value" value="666666"/>
        </map>
    </property>
</bean>

使用p命名空间简化配置(了解)

是bean中的属性,在使用之前需要在beans标签中添加对应的空间支持

xmlns:p="http://www.springframework.org/schema/p"

具体使用:

之前:
<bean id="userService" class="com.lcl.service.impl.UserServiceImpl">
     <property name="userDao" ref="userDao"/>
     <property name="bookDao" ref="bookDao"/>
 </bean>
使用p命名空间:
<bean
        id="userService"
        class="com.lcl.service.impl.UserServiceImpl"
        p:userDao-ref="userDao"
        p:bookDao-ref="bookDao"
/>

SpEL(了解)

spring提供了对EL表达式的支持,统一属性注入格式

是value属性的属性值

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3Gagg4B2-1656900398012)(C:\Users\StarrySea\Desktop\MarkDown学习\图片\spring3.jpg)]

properties文件

spring提供了读取外部properties文件机制,使用读取到的数据为bean的属性赋值。例如给set注入的非引用资源赋值

操作步骤:

1、准备外部properties文件

2、开启context命名空间支持

   <!--1、加载context命名空间支持-->
    <!--xmlns:context="http://www.springframework.org/schema/context"-->
    <!--还需要在xsi:schemaLocation="",双引号内加
       http://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd
       也就是把原有的复制一下,把bean改为context
    -->

3、加载指定的properties文件

<!--2、加载配置文件,*.properties表示加载全部-->
<context:property-placeholder location="classpath:*.properties"/>

4、使用加载的数据

<bean id="userDao" class="com.lcl.dao.impl.UserDaoImpl">
    <property name="username" value="${username}"/>
    <property name="pwd" value="${pwd}"/>
</bean>

注意:

若是要要加载所有properties文件,可以使用*.properties。

读取数据使用${propertiesName},propertiesName指properties文件的属性名

团队开发,import导入配置文件(.xml文件,也就是其他配置bean的文件,主配置文件和子配置文件)

import是标签,归属于beans标签,作用是在当前配置文件中导入其他配置文件中的项

属性:

resource:加载的配置文件名

<!--import导入子配置文件。将上面的配置拆分成两个子配置文件,再导入进来-->
<import resource="applicationContext-book.xml"/>
<import resource="applicationContext-user.xml"/>

也可以使用下面这方式,主方法加载配置文件时。但很少用

//加载两个配置文件一起起作用
//ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext-book.xml","applicationContext-user.xml");

bean定义冲突问题:同id的bean,后定义的覆盖先定义的,import导入顺序也是

BeanFactory是顶层接口,ApplicationContext是下级接口,前者创建的bean采用【延迟加载】,使用才创建。而后者创建bean默认采用【立即加载】形式

第三方资源配置

例如阿里云数据源方案Druid

<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://localhost:3306/spring_db"/>
    <property name="username" value="root"/>
    <property name="password" value="root"/>
</bean>

综合案例:使用spring整合mybatis技术,完成账户模块(Account)的基础增删改查

最后有个错误,不知道啥情况:Exception in thread “main” org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.lcl.dao.AccountDao.save

APP类是测试类

import com.lcl.domain.Account;
import com.lcl.service.AccountService;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class APP {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");

        AccountService accountService = (AccountService) ctx.getBean("accountService");

       /* Account ac = accountService.findById(1);
        System.out.println(ac);*/

        Account account = new Account();
        account.setName("Tom");
        account.setMoney(111.111);

        accountService.save(account);
    }
}

domian包是实体类(略,根据数据表创建)

dao包下的接口是对应的数据库增删改查 的方法

package com.lcl.dao;

import com.lcl.domain.Account;

import java.util.List;

public interface AccountDao {
    void save(Account account);

    void delete(Integer id);

    void update(Account account);

    List<Account> findAll();

    Account findById(Integer id);
}

service包下也有个接口和上面的一毛一样,还有个实现类,实现接口中的增删改查,获取数据

package com.lcl.service.impl;

import com.lcl.dao.AccountDao;
import com.lcl.domain.Account;
import com.lcl.service.AccountService;

import java.util.List;

public class AccountServiceImpl implements AccountService {
    private AccountDao accountDao;

    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }

    public void save(Account account) {
        accountDao.save(account);
    }

    public void delete(Integer id) {
        accountDao.delete(id);
    }

    public void update(Account account) {
        accountDao.update(account);
    }

    public List<Account> findAll() {
        return accountDao.findAll();
    }

    public Account findById(Integer id) {
        return accountDao.findById(id);
    }
}

resource包下有个com.lcl.dao包里面有个AccountDao.xml配置文件,设置增删改查的语句

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.lcl.dao.AccountDao">

    <select id="findById" resultType="account" parameterType="int">
        select * from account where id = #{id}
    </select>

    <select id="findAll" resultType="account">
        select * from account
    </select>

    <insert id="save" parameterType="account">
        insert into account(name,money) values (#{name},#{money})
    </insert>

    <delete id="delete" parameterType="int">
        delete from account where id = #{id}
    </delete>

    <update id="update" parameterType="account">
        update account set name = #{name},money=#{money} where id = #{id}
    </update>

</mapper>

还有一个jdbc.properties的文件,里面有数据库连接消息

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://192.168.23.129:3306/spring_db
username=root
password=root

最终要的还是applicationContext.xml,配置bean资源

<?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
">
    <!--加载properties配置文件信息-->
    <context:property-placeholder location="classpath:jdbc.properties"/>

    <!--加载druid信息-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${driver}"/>
        <property name="url" value="${url}"/>
        <property name="username" value="${username}"/>
        <property name="password" value="${password}"/>
    </bean>

    <!--配置service为spring的bean,并在service用set注入dao-->
    <bean id="accountService" class="com.lcl.service.impl.AccountServiceImpl">
        <property name="accountDao" ref="accountDao"/>
    </bean>


    <!--spring整合mybatis后控制的【创建连接】的对象-->
    <bean class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="typeAliasesPackage" value="com.lcl.domain"/>
    </bean>

    <!--加载mybatis【映射配置】的扫描,将其作为spring的bean进行管理-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.lcl.dao"/>
    </bean>

</beans>

pom.xml添加依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.lcl</groupId>
    <artifactId>Spring_day01_04_综合案例spring整合mybatis</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.19.RELEASE</version>
        </dependency>

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

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.16</version>
        </dependency>

        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.3</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>

        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>1.3.0</version>
        </dependency>
    </dependencies>

</project>

注解开发

<!--启动注解驱动,扫描包和下级包,一直往下扫-->
<context:component-scan base-package="com.lcl"/>

在类上的注解

@Component("userService")   //指定id,对应的类就是bean资源了,若是没写id,且有同类型的也没写id,那么别的类注入时,无法区分。可以使用@Primary来确定优先加载,这个玩意只能用一次,不然也无法区别
@Scope("prototype") //指定作用范围,单例或非单例,一般只写scope,默认是单例  

在初始化和销毁的方法上的注解,设定声明周期

@PostConstruct
public void init(){
    System.out.println("user service init");
}
@PreDestroy
public void destroy(){
    System.out.println("user service destroy");
}

加载第三方资源,那个@Component不合适,把它干掉,应该在纯注解那个的SpringConfig那儿加一个@Import(JDBCConfig.class)

//@Component
public class JDBCConfig {
    @Bean("dataSource")
    public static DruidDataSource getDataSource(){
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName("com.mysql.jdbc.Driver");
        ds.setUrl("jdbc:mysql://localhost:3306/spring_db");
        ds.setUsername("root");
        ds.setPassword("root");
        return ds;
    }
}

bean非引用类型属性的注入,在成员变量上,或set方法上加注解都可以。重要的是,以后可以不用写set方法了,直接在成员变量上加注解就行了

//@Value("3")
private int num;
@Value("lcl.1.0.0")
private String version;

@Value("3")
public void setNum(int num) {
    this.num = num;
}

public void setVersion(String version) {
    this.version = version;
}

bean引用类型属性的注入

//bean引用类型属性的注入,也可以不写set方法,@Autowired系统自己去找  先类型,后bookDao名  匹配的bean资源,若是同名或没有对应名字则报错。
//可以使用@Qualifier指定bean
@Autowired
//@Qualifier("bookDao")
private BookDao bookDao;

加载properties文件的属性,类上加 @PropertySource(“classpath:文件名”),若是有多个properties文件,不能用*,用大括号,引用多个。ignoreResourceNotFound = true表示文件不存在则忽略,不会报错。

这个可以放在任何地方,其他地方也可以用,以后就放在SpringConfig类上

@PropertySource(value={"classpath:jdbc.properties","classpath:abc.properties"},ignoreResourceNotFound = true)

成员变量上加@Value(“${属性名}”)

@Component("userDao")
@PropertySource("classpath:jdbc.properties")
public class UserDaoImpl implements UserDao {
    //加载properties文件属性
    @Value("${jdbc.userName}")
    private String userName;
    @Value("${jdbc.password}")
    private String password;


    public void save(){
        System.out.println("user dao is running"+userName+password);
    }
}

纯注解开发

package com.lcl.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

//纯注解开发,干掉.xml文件。下面就是干掉了applicationContext.xml

@Configuration  //干掉beans那一坨
@ComponentScan("com.lcl")  //干掉 <context:component-scan base-package="com.lcl"/>

@Import(JDBCConfig.class)//第三方资源bean,用大括号加入多个
public class SpringConfig {
}

测试类中

ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
不用那个加载配置文件对象了,加载上面那个类

依赖加载

1、@DependsOn(“beanId”),在类或是方法上,控制【加载顺序】,加载有这个注解的类前,需要先加载它依赖的beanId资源。

2、@Order(value),这是配置类注解,在多个【配置类】前加这个,value从1开始,越小越先加载

3、@Lazy,在类或方法上,叫延迟加载

整合第三方技术-注解整合mybatis

主要把.xml文件给干掉

首先干掉 AccountDao.xml

package com.lcl.dao;

import com.lcl.domain.Account;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;

import java.util.List;

public interface AccountDao {
    @Insert("insert into account(name,money) values (#{name},#{money})")
    void save(Account account);

    @Delete("delete from account where id = #{id}")
    void delete(Integer id);

    @Update("update account set name = #{name},money = #{money} where id = #{id}")
    void update(Account account);

    @Select("select * from account")
    List<Account> findAll();

    @Select("select * from account where id = #{id}")
    Account findById(Integer id);
}

再干掉applicationContext.xml

package com.lcl.conifg;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.PropertySource;

@Configuration
@ComponentScan("com.lcl")
@PropertySource("classpath:jdbc.properties")
@Import({JDBCConfig.class,MyBatisConfig.class})
public class SpringConfig {
}
package com.lcl.conifg;

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;


public class JDBCConfig {
    @Value("${jdbc.driver}")
    private String driver;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String userName;
    @Value("${jdbc.password}")
    private String password;

    @Bean("dataSource")
    public DruidDataSource getDataSource(){//不加static
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName(driver);
        ds.setUrl(url);
        ds.setUsername(userName);
        ds.setPassword(password);
        return ds;
    }
}
package com.lcl.conifg;

import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;

import javax.sql.DataSource;

public class MyBatisConfig {
    @Bean
    public SqlSessionFactoryBean getSqlSessionFactoryBean(@Autowired DataSource dataSource){
        SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean();
        ssfb.setTypeAliasesPackage("com.lcl.domain");
        ssfb.setDataSource(dataSource);
        return ssfb;
    }

    @Bean
    public MapperScannerConfigurer getMapperScannerConfigurer(){
        MapperScannerConfigurer msc = new MapperScannerConfigurer();
        msc.setBasePackage("com.lcl.dao");
        return msc;
    }
}

注解整合Junit测试

添加依赖坐标

  <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>5.1.9.RELEASE</version>
    </dependency>
</dependencies>

在test包下写测试类测试方法

//设定spring专用的类加载器
@RunWith(SpringJUnit4ClassRunner.class)
//设定加载的spring上下文对应的配置
@ContextConfiguration(classes = SpringConfig.class)

package com.lcl.service;

import com.lcl.conifg.SpringConfig;
import com.lcl.domain.Account;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

//设定spring专用的类加载器
@RunWith(SpringJUnit4ClassRunner.class)
//设定加载的spring上下文对应的配置
@ContextConfiguration(classes = SpringConfig.class)
public class UserServiceTest {
    @Autowired
    private AccountService accountService;

    @Test
    public void testFindById(){
        Account ac = accountService.findById(2);
        //System.out.println(ac);
        Assert.assertEquals("jock",ac.getName());
    }
}

IoC底层核心原理

1、核心接口

2、组件扫描:开发过程中,需要根据需求加载必要的bean,排除指定的bean

设定组件扫描加载过滤器:

1)、按注解过滤,SpringConfig类中加:

@ComponentScan(
        value = "com.lcl",
        //排除指定的bean,使之不被加载
        excludeFilters = @ComponentScan.Filter(  //设置过滤器
                type = FilterType.ANNOTATION,   //五种策略,此处是按注解过滤
                classes = Service.class     //过滤所有Service(可改为其他)修饰的bean
        )
)

2)、自定义类型过滤

先写个类实现Filter接口,自定义类型

public class MyTypeFilter implements TypeFilter {
    @Override
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
        //加载的类满足要求,匹配成功
        ClassMetadata classMetadata = metadataReader.getClassMetadata();
        String className = classMetadata.getClassName();
        System.out.println(className);
        if(className.equals("com.lcl.service.impl.UserServiceImpl")){
            return true;//拦截
        }
        return false;//全部放行
    }
}

再用过滤器过滤,SpringConfig类中加:

@ComponentScan(
        value = "com.lcl",
        //自定义类型
        excludeFilters = @ComponentScan.Filter(
                type = FilterType.CUSTOM,
                classes = MyTypeFilter.class
        )
)

3、自定义导入器:

bean只有通过配置才可以进入spring容器,被spring加载并控制,bean配置有两种方式:一是XML的bean标签,二是@Component以及衍生的注解配置。

企业开发中,通常需要配置【大量】的bean,需要一种快速高效配置大量bean的方法,即自定义导入器,定义一个类,实现ImportSelector接口

//自定义导入器
public class MyImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        //单个bean,从配置文件获取的字符串不需要分割
       /* ResourceBundle bundle = ResourceBundle.getBundle("import");//根据文件名获取
        String className = bundle.getString("className");
        return new String[]{className};*/


        //多个bean,从配置文件获取的字符串需要分割
        ResourceBundle bundle = ResourceBundle.getBundle("import");
        String className = bundle.getString("className");
        return className.split(",");

        //return new String[]{"com.lcl.dao.impl.BookDaoImpl"};//上面用配置文件,此处是直接使用
    }
}

配置文件

#className=com.lcl.dao.impl.BookDaoImpl  单个

#className=com.lcl.dao.impl.BookDaoImpl,com.lcl.dao.impl.AccountDaoImpl  多个

#通配,包下所有的
path=com.lcl.dao.impl.*  

SpringConfig类中加:

@Import(MyImportSelector.class)//自定义导入器的使用

还有通过路径通配,包下所有都可以(直接拿来使用即可,不必记住)

SpringConfig类中加:

@Import(CustomerImportSelector.class)

定义一个类,实现ImportSelector接口:

package config.selector;

import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.io.support.PropertiesLoaderUtils;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.filter.AspectJTypeFilter;
import org.springframework.core.type.filter.TypeFilter;

import java.io.IOException;
import java.util.HashSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

public class CustomerImportSelector implements ImportSelector {

    private String expression;

    public CustomerImportSelector() {
        try {
            //初始化时指定加载的properties文件名
            Properties loadAllProperties = PropertiesLoaderUtils.loadAllProperties("import.properties");
            //设定加载的属性名
            expression = loadAllProperties.getProperty("path");
        } catch (IOException e) {
            //TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {

        //1.定义扫描包的名称
        String[] basePackages = null;
        //2.判断有@Import注解的类上是否有@ComponentScan注解
        if (importingClassMetadata.hasAnnotation(ComponentScan.class.getName())) {
            //3.取出@ComponentScan注解的属性
            Map<String, Object> annotationAttributes = importingClassMetadata.getAnnotationAttributes(ComponentScan.class.getName());
            //4.取出属性名称为basePackages属性的值
            basePackages = (String[]) annotationAttributes.get("basePackages");
        }
        //5.判断是否有此属性(如果没有ComponentScan注解则属性值为null,如果有ComponentScan注解,则basePackages默认为空数组)
        if (basePackages == null || basePackages.length == 0) {
            String basePackage = null;
            try {
                //6.取出包含@Import注解类的包名
                basePackage = Class.forName(importingClassMetadata.getClassName()).getPackage().getName();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
            //7.存入数组中
            basePackages = new String[]{basePackage};
        }
        //8.创建类路径扫描器
        ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
        //9.创建类型过滤器(此处使用切入点表达式类型过滤器)
        TypeFilter typeFilter = new AspectJTypeFilter(expression, this.getClass().getClassLoader());
        //10.给扫描器加入类型过滤器
        scanner.addIncludeFilter(typeFilter);
        //11.创建存放全限定类名的集合
        Set<String> classes = new HashSet<>();
        //12.填充集合数据
        for (String basePackage : basePackages) {
            scanner.findCandidateComponents(basePackage).forEach(beanDefinition -> classes.add(beanDefinition.getBeanClassName()));
        }
        //13.按照规则返回
        return classes.toArray(new String[classes.size()]);
    }

}

4、自定义注册器

//自定义注册器
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry,false);

        //scanner.addExcludeFilter();
        scanner.addIncludeFilter(new TypeFilter() {//定义过滤器规则
            @Override
            public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
                return true;//全部要,都加载
            }
        });
        scanner.scan("com.lcl");
    }
}

SpringConfig类中加:

@Import(MyImportBeanDefinitionRegistrar.class)//自定义注册器,把@ComponentScan("com.lcl")干掉

5、bean初始化过程解析

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-T9HIThBy-1656900398012)(C:\Users\StarrySea\Desktop\MarkDown学习\图片\spring4.jpg)]

package config.postprocessor;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;

public class MyBeanFactory implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
        System.out.println("bean工厂制作好了,好友什么事情需要处理");
    }
}
package config.postprocessor;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

public class MyBean implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("bean之前。。。。。");
        System.out.println(beanName);
        return null;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("bean之后。。。。。");
        return null;
    }
}
package com.lcl.dao.impl;

import com.lcl.dao.AccountDao;
import org.springframework.beans.factory.InitializingBean;

public class AccountDaoImpl implements AccountDao, InitializingBean {
    @Override
    public void save() {
        System.out.println("Account Dao running......");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("AccountDaoImpl......初始化");
    }
}

SpringConfig类中用@Import导入相应的类

​ AOP

AOP:面向切面编程,一种编程【范式】,隶属于软工范畴,指导开发者如何组织程序结构。基于OOP基础上进行横向开发,一切围绕共性功能进行,完成某个任务构建可能遇到的所有【共性功能】

从各个行业的标准化、规范化入手,将所有共性功能逐一开发完毕,最终以【功能组合】来完成开发,实现“插拔式组件体系结构”,走向半自动化/全自动化

相关概念:1、连接点:所有方法

​ 2、切入点:具有共性功能的方法

​ 3、通知:将共性功能的方法挖出来变成的功能模块

​ 4、切面:切入点和通知的关系,也就是从哪挖的通知,以后

​ 放回原来的切入点,位置和共性功能的关系

​ 5、通知类型:通知概念的一部分,表明通知放在哪,前啊还

​ 是后啊,具体位置

​ 6、目标对象:也就是挖去共性功能的简化版的方法

​ 7、织入:将共性功能放回目标对象的过程即为织入

​ 8、代理:上面的共性功能织入目标对象最后生成的类

​ 9、引入:代理类可以自己无中生有一些成员变量/方法

AOP开发过程:1、正常的制作程序 2、将非共性功能开发到对应的目标对象类中,并制作成切入点方法 3、将共性功能独立开发出来,制作【通知】4、在配置文件中,声明【切入点】5、在配置文件中,声明切入点与通知的关系(含通知类型),即【切面】

后面的织入、代理等等由spring自动完成

AOP入门方法方式:XML、XML+注解、注解

入门案例制作分析:1、导入相关坐标 2、确认要抽取的功能,将其制作成方法保存到专用的类中,删除原始业务中对应的功能 3、将所有进行AOP操作的资源加载到IoC容器中 4、使用配置文件的方式描述被抽取功能的位置,并描述被抽取功能与对应位置的关系 5、运行程序

1、

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.1.19.RELEASE</version>
    </dependency>

    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.9.4</version>
    </dependency>
</dependencies>

2、

package com.lcl.service.impl;

import com.lcl.service.UserService;

public class UserServiceImpl implements UserService {
    @Override
    public void save() {
        //System.out.println("这是共性功能!");  挖走
        System.out.println("user service running......");
    }
}
package com.lcl.aop;

public class AOPAdvice {
    public void function(){
        System.out.println("这是共性功能!");
    }
}

3、4、

<?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:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       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/context
       https://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/aop
       https://www.springframework.org/schema/aop/spring-aop.xsd
">

    <!--上面的beans中要加入三行有关aop的 内容-->

    <bean id="userService" class="com.lcl.service.impl.UserServiceImpl"/>

    <!--通知,作为bean资源放入IoC容器中-->
    <bean id="myAdvice" class="com.lcl.aop.AOPAdvice"/>

    <!--aop配置,指定切入点、通知,以及切面-->
    <aop:config>
        <!--切入点,id随便取-->
        <aop:pointcut id="pt" expression="execution(* *..*(..))"/>
        <!--切面,指定通知bean-->
        <aop:aspect ref="myAdvice">
            <!--将共性功能对应的方法function织入切入点中,通知类中某一个共性功能方法织入对应的切入点-->
            <aop:before method="function" pointcut-ref="pt"/>
        </aop:aspect>
    </aop:config>
</beans>

一、AOP配置(XML)

<aop:config>
    <aop:pointcut id="pt" expression="execution(* *..*(..))"/>
    <aop:aspect ref="myAdvice">
        <aop:before method="before" pointcut-ref="pt"/>
    </aop:aspect>
</aop:config>
<aop:config></aop:config>
设置AOP,可以有多个,都生效
<aop:aspect ref="myAdvice"></aop:aspect>
切面,设置具体的AOP通知对应的切入点,ref对应上面的bean资源,也就是通知类,通知类里面是共性功能对应方法。可以有多个,同时生效
<aop:pointcut id="pt" expression="execution(* *..*(..))"/>
切入点。id随便,一般就pt,expression是对切入点的描述(切入点表达式),也就是位置
<aop:before method="before" pointcut-ref="pt"/>
method即通知类里面是共性功能对应方法,pointcut-ref切入点

切入点表达式的组成:

关键字(访问修饰符 返回值 包名.类名.方法名(参数) 异常名)

关键字:描述表达式的匹配模式(参照关键字列表)

访问修饰符:方法的访问控制权限修饰符

类名:方法所在的类(此处可以配置接口名称)

异常:方法定义中指定抛出的异常

范例:

execution(public User com.lcl.service.UserService.findById(int))

通配符:1、*:单个独立的任意符号,可以单独出现,也可以作为前缀或后缀的匹配符出现 2、… :多个连续的任意符号,可以单独出现,常用于简化包名与参数的书写 3、+:专用于匹配子类类型

范例作为一个整体,可以用&&、||、!来连接多个表达式

<!--切入点的三种配置-->
<aop:config>
    <!--<aop:pointcut id="pt" expression="execution(* com.lcl.service.UserService.save())"/>-->
    <!--公共切入点-->
    <aop:pointcut id="pt" expression="execution(* *..*())"/>
    <aop:aspect ref="myAdvice">
        <!--局部切入点-->
        <aop:pointcut id="pt2" expression="execution(* *..*())"/>

        <!--直接加-->
        <aop:before method="before" pointcut="execution(* *..*())"/>

        <!--<aop:before method="before" pointcut-ref="pt2"/>-->
    </aop:aspect>
</aop:config>

通知类型

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jlGCgv7s-1656900398013)(C:\Users\StarrySea\AppData\Roaming\Typora\typora-user-images\image-20220113213028298.png)]

<aop:config>
    <aop:pointcut id="pt" expression="execution(* *..*())"/>
    <aop:aspect ref="myAdvice">
        <!--<aop:before method="before" pointcut-ref="pt"/>-->
        <!--<aop:after method="after" pointcut-ref="pt"/>-->
        <!--<aop:after-returning method="afterReturning" pointcut-ref="pt"/>-->
        <!--<aop:after-throwing method="afterThrowing" pointcut-ref="pt"/>-->
        <aop:around method="around" pointcut-ref="pt"/>
    </aop:aspect>
</aop:config>
package com.lcl.aop;

import org.aspectj.lang.ProceedingJoinPoint;

public class AOPAdvice {
    public void before(){

        System.out.println("before");
    }
    public void after(){
        System.out.println("after");
    }
    public void afterReturning(){
        System.out.println("afterReturning");
    }
    public void afterThrowing(){
        System.out.println("afterThrowing");
    }
    public void around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("before");
        //对原始方法的调用,如果没有这个,则原方法不执行!!!
        //因为无法预知原方法是否会出异常,需要抛出异常
        pjp.proceed();
        System.out.println("after");
    }


}

通知顺序:当同一个切入点配置了多个通知时,通知会存在运行的先后顺序,该顺序以通知配置的顺序为准

通知获取原始方法的数据:参数、返回值、异常

1、通知获取参数数据(给原始方法参数,调用时传实参,该实参就会被下面的通知获取)

方法一(五种通知通用):

public void before(JoinPoint jp){
    Object[] args = jp.getArgs();
    System.out.println("before..."+args[0]);
}

方法二(了解即可):

public void before1(int x){
    System.out.println("before1..."+x);
}
<aop:before method="before1" pointcut="execution(* *..*(int)) &amp;&amp; args(x)"/>

x:是强绑定的,对应通知的参数,不可变
如果是多个参数,将上面的改一下即可<aop:before method="before1" pointcut="execution(* *..*(int,int)) &amp;&amp; args(x,y)"/>
其中有个arg-names的属性,可以改变,多个参数的顺序,按照arg-names的值排序,例如arg-names="y,x",那么就是按照y,x,而不是x,y了

2、通知获取返回值数据(只有after-returning、around可以使用,能获取返回值。原方法得有返回值才行)

after-returning:

public void afterReturning(Object ret){
    System.out.println("afterReturning..."+ret);
}
<aop:after-returning method="afterReturning" pointcut-ref="pt" returning="ret"/>
加一个returning属性,值对应通知方法中的参数Object ret

around:

public Object around1(ProceedingJoinPoint pjp) throws Throwable {
    System.out.println("before...");
    //对原始方法的调用
    Object ret = pjp.proceed();
    System.out.println("after..."+ret);
    return ret;
}

配置文件的aop不变

3、通知中获取异常对象(只有after-throwing、around可以使用)

after-throwing:

public void afterThrowing1(Throwable t){
    System.out.println("afterThrowing..."+t.getMessage());
}
<aop:after-throwing method="afterThrowing1" pointcut-ref="pt" throwing="t"/>
多个throwing属性,值对应通知方法的参数Throwable t

around:

public Object around2(ProceedingJoinPoint pjp) {
    System.out.println("around before...");
    //对原始方法的调用
    Object ret = null;
    try {
        ret = pjp.proceed();
    } catch (Throwable throwable) {
        System.out.println("around...exception...."+throwable.getMessage());
    }
    System.out.println("around after..."+ret);
    return ret;
}
原本的异常抛出删除,用try catch。配置文件不变

二、AOP配置(注解+XML)

一、在配置文件中开启AOP注解

<aop:aspectj-autoproxy/>

二、在切面类,也就是通知的那个类中使用注解

package com.lcl.aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Component
@Aspect   //1、切面类注解,对应<aop:aspect ref="myAdvice">
public class AOPAdvice {

    @Pointcut("execution(* *..*(..))")//2、切点注解,对应<aop:pointcut id="pt" expression="execution(* *..*(..))"/>
    public void pt(){}

    @Before("pt()")//3、对应<aop:before method="before" pointcut-ref="pt"/>
    public void before(){
        System.out.println("before...");
    }

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

    @AfterReturning("pt()")
    public void afterThrowing(){
        System.out.println("afterReturning...");
    }

    @AfterThrowing("pt()")
    public void afterThrowing1(){
        System.out.println("afterThrowing...");
    }

    @Around("pt()")
    public Object around1(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("around before...");
        Object ret = pjp.proceed();
        System.out.println("around after..."+ret);
        return ret;
    }



}

注意:

1、切入点最终体现为一个方法pt(),无参无返回值,无方法体,不能为抽象方法

2、引入切入点时必须使用方法调用名称,方法后的()不能省略

3、切面类内定义的切入点pt()只能在当前类使用,想引用,使用 类名.方法名()

4、可以在注解中加参数,实现XML配置的属性,例如returning属性

@AfterReturning(value="pt()",returning="ret")

AOP使用XML配置下,通知的执行顺序按照配置顺序决定,使用注解配置,则参照通知所配置的方法名字字符串对应的编码顺序,可以理解为字母顺序。

1、同一个类中,相同的通知类型,以方法名排序

2、不同通知类(切面类),以类名排序

3、使用@Order注解通过变更bean的加载顺序改变通知的加载顺序

通知名由三部分组成,前缀、顺序编码、功能描述。执行顺序由顺序编码控制

三、AOP配置(纯注解)

pom文件加两个依赖,测试时使用

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

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>5.1.9.RELEASE</version>
</dependency>

在spring注解配置类SpringConfig上加一个@EnableAspectJAutoProxy

@Configuration//代替beans
@ComponentScan("com.lcl")//代替<context:component-scan base-package="com.lcl"/>
@EnableAspectJAutoProxy//代替<aop:aspectj-autoproxy/>
public class SpringConfig {
}

AOP底层原理

1、静态代理

1)装饰者模式:在不惊动原始设计的基础上,为其添加功能

两个类实现同一个接口,一个类是原始功能,另一个类,则有原始功能类对象作为成员变量,还需要构造有参构造,在实现方法中,调用原始功能并添加新功能

public interface UserService {
    public void save();
}
public class UserServiceImpl implements UserService {
    public void save() {
        System.out.println("水泥墙");
    }
}
public class UserServiceImplDecorator implements UserService {
    private UserService userService;

    public UserServiceImplDecorator(UserService userService) {
        this.userService = userService;
    }

    public void save() {
        userService.save();//原始功能
        //增强功能
        System.out.println("刮大白");
    }
}

2、动态代理——JDK Proxy

针对对象做代理,要求原始对象具有接口实现,并对接口方法进行增强

public class UserServiceJDKProxy {
    public static UserService createUserServiceJDKProxy(final UserService userService){
        ClassLoader classLoader = userService.getClass().getClassLoader();//类加载器
        Class[] interfaces = userService.getClass().getInterfaces();//所有接口
        InvocationHandler invocationHandler = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Object ret = method.invoke(userService,args);
                System.out.println("刮大白");
                System.out.println("贴壁纸");
                return ret;
            }
        };
        UserService service = (UserService)Proxy.newProxyInstance(classLoader,interfaces,invocationHandler);
        return service;
    }
}

3、动态代理——CGLIB:Code生成类库

CGLIB动态代理不限定是否具有接口,可以对任意操作进行增强

CGLIB动态代理不需要原始被代理对象,动态创建新的代理对象

package base.cglib;

import com.lcl.service.UserService;
import org.springframework.aop.TargetSource;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class UserServiceCglibProxy {
    public static UserService createUserServiceCglibProxy(Class clazz){
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(clazz);//参数clazz变成了enhancer的父类
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                //o:是最后被代理出来的对象
                Object ret = methodProxy.invokeSuper(o,objects);//对原始方法的调用
                //此方法能为所有方法增强,若是要指定save方法,用if判断
                if(method.getName().equals("save")){
                    System.out.println("刮大白");
                    System.out.println("贴壁纸");
                }

                return ret;
            }
        });

        UserService userService = (UserService) enhancer.create();
        return userService;
    }
}

内存中的字节码继承某一个类,由字节码生成对象,拦截原始功能前后都能增强

AOP配置中,默认使用JDKProxy,属性值改为true,则为Cglib.

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-r0us5EvB-1656900398013)(C:\Users\StarrySea\AppData\Roaming\Typora\typora-user-images\image-20220115145913267.png)]

Srping事务核心对象:

J2EE开发使用分层设计的思想进行,对于简单的业务层转调数据层的单一操作,事务开启在业务层或者数据层并无太大差异,当业务中包含多个数据层的调用时,需要【在业务层开启事务】,对数据层多个操作进行组合归并属于一个事务进行处理

Spring为业务层提供了整套的事务解决方案(下面三个都是接口):

1、PlatformTransactionManager

2、TransactionDefinition

3、TransactionStatus

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zSDbMQQr-1656900398013)(C:\Users\StarrySea\AppData\Roaming\Typora\typora-user-images\image-20220115200946511.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0hjOtwKL-1656900398014)(C:\Users\StarrySea\AppData\Roaming\Typora\typora-user-images\image-20220115201021349.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wbmy4IGR-1656900398014)(C:\Users\StarrySea\AppData\Roaming\Typora\typora-user-images\image-20220115201045881.png)]

事务控制方式:编程式、声明式(XML)、声明式(注解)

1、编程式

    public void transfer(String outName, String inName, Double money) {
        //开启事务,PlatformTransactionManager接口定义了事务的基本操作,获取、提交、回滚事务
        //上面需要注入一个private DataSource dataSource资源,指定数据源(数据库),然后传参给下面
        PlatformTransactionManager ptm = new DataSourceTransactionManager(dataSource);
        //事务定义对象,TransactionDefinition接口定义事务的基本信息,传给事务状态,此处是默认对象DefaultTransactionDefinition
        DefaultTransactionDefinition td = new DefaultTransactionDefinition();
        //事务状态,TransactionStatus接口定义事务在某个时间点上的状态信息和状态操作
        TransactionStatus ts = ptm.getTransaction(td);

        accountDao.inMoney(outName,money);
        int i = 1/0;
        accountDao.outMoney(inName,money);

        //提交事务,参数是事务状态
        ptm.commit(ts);
    }
}

上面的可以用AOP改造(这里用注解有问题,未解决,还是用xml)

@Component
@Aspect
public class TxAdvice {

    @Autowired
    private DataSource dataSource;

    @Pointcut("execution(* *..*(..))")
    public void pt(){}

    @Around("pt()")
    public Object tx(ProceedingJoinPoint pjp) throws Throwable {
        PlatformTransactionManager ptm = new DataSourceTransactionManager(dataSource);
        DefaultTransactionDefinition td = new DefaultTransactionDefinition();
        TransactionStatus ts = ptm.getTransaction(td);
        //调用原方法
        Object ret = pjp.proceed(pjp.getArgs());
        ptm.commit(ts);

        return ret;
    }
}

2、声明式(XML):干掉AOP那个类,或者说干掉编程式事务相关操作

先需要在beans加三行开启tx

再加

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BZ5jVrDr-1656900398014)(C:\Users\StarrySea\AppData\Roaming\Typora\typora-user-images\image-20220117104622223.png)]

事务传播行为:描述的是事务协调员(编程式代码中,两个accountDao方法,在里面)对事务管理员(编程式代码中,对事务相关的操作,在外面)所携带事务的处理态度

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SwPn3jzX-1656900398015)(C:\Users\StarrySea\AppData\Roaming\Typora\typora-user-images\image-20220117105602497.png)]

3、声明式(注解):干掉声明式(XML)图中内容(dataSource不动)

先加<tx:annotation-driven transaction-manager=“txManage”/>

再在

@Component("accountService")
public class AccountServiceImpl implements AccountService {
    @Autowired
    private AccountDao accountDao;

    public void transfer(String outName, String inName, Double money) {
        //AOP改造
        accountDao.inMoney(outName,money);
        //int i = 1/0;
        accountDao.outMoney(inName,money);

    }
}

tansfer方法上加@Transactional

其实最好配在接口中(接口上或方法上),不要配在实现类中

纯注解看看代码,在业务层接口上加@Transactional,在SpringConfig类上加@EnableTransactionManagement,最后在JDBCConfig类中加

@Bean
public PlatformTransactionManager getTransactionManager(DataSource dataSource){
    return new DataSourceTransactionManager(dataSource);
}

上面这个方法可以写个类,放在类里面

Spring模板对象:哎嘿,没记笔记,气不气。。。。。。我懒!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值