猿创征文 | Spring知识大全(Spring,看这一篇就够了)

Spring

*核心概念:

  • IOC(Inversion of Control)控制反转:
    目的:为了降低程序间的耦合度
    含义:把程序内主动new创造对象的过程交给外部程序去做,创建对象的过程中控制权由程序部转到了*外部*去实现。
    Spring对Ioc思想进行了实现:其提供了IOC容器来充当IOC思想的“外部”,IOC负责创建和管理对象,被创建和管理的对象在IOC中称为Bean
  • DI(Dependence Injection) 依赖注入:
    含义:在容器中建立bean之间的依赖关系。
  • 效果:使用对象是不仅可以直接从容器中取,还能获取到bean绑定的所有依赖关系。

1:IOC入门案例:

  1. ​ 在pom.xml 配置文件中导入依赖

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.2.10.RELEASE</version>
    </dependency>
    
  2. ​ 在resourse文件夹中建立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 http://www.springframework.org/schema/beans/spring-beans.xsd">
            <!--1:导入spring的坐标spring-context,对应版本为5.2.10.RELEASE-->
    
            <!--2:配置bean-->
            <!--bean:配置bean-->
            <!--id:给bean起的别名-->
            <!--class:给bean定义类型-->
        <bean id="bookDao" class="com.ysj.dao.impl.BookDaoImpl">
    
        </bean>
    
        <bean id="bookService" class="com.ysj.service.impl.BookServiceImpl">
    
        </bean>
    </beans>
    
  3. 在测试类中:

                //3:加载配置文件,获取IOC容器对象
       ApplicationContext ext = new ClassPathXmlApplicationContext("applicationContext.xml");
                //4:获取bean
         //  BookDao bookDao = (BookDao) ext.getBean("bookDao");
         // bookDao.save();;
            BookService bookService = (BookService) ext.getBean("bookService");
            bookService.save();
    
  4. 入门案例中,此时在业务层实现中,仍存在new创建对象,耦合度相对来说仍然较高,接下来就到DI(Dependence Injection) 登场了。

2:DI(Dependence Injection)入门案例:

  1. //7:删除new
    private BookDao bookDao;
    
    public void save() {
        System.out.println("book service save ...");
        bookDao.save();
    }
    
    //8:设置setter方法:容器创建对象会调用setter方法
    public void setBookDao(BookDao bookDao) {
        this.bookDao = bookDao;
    }
    

    <bean id="bookService" class="com.ysj.service.impl.BookServiceImpl">
    <!-- 配置server与dao的关系(dao对象在server中创建,所以在server中配置)-->
    <!--  property:配置当前bean的属性,即对应类中的属性-->
    <!--  name:当前bean所对应的类中的具体属性-->
    <!--  ref:表示参照哪个bean进行配置-->
        <property name="bookDao" ref="bookDao"></property>
    </bean>

3:bean

3:bean的基础配置

3.1:name属性:给bean起别名,多个别名间用空格隔开
  • <bean id="bookDao" name="dao ss ss1"  class="com.ysj.dao.impl.BookDaoImpl">
    
3.2:bean默认是单例模式
<!--  scope:选择bean是否为单例模式
		prototype:多例
		singleton:单例-->
<bean id="bookDao" name="dao ss ss1" scope="prototype"
      class="com.ysj.dao.impl.BookDaoImpl">
3.3🏖bean默认是单例模式的原因
如果默认不是单例,那么创造的bean对象将会无穷无尽,增加了Spring的负担
  ##适合交给容器进行管理的bean
  1:表现层对象
  2:业务层对象
  3:数据层对象
  4:工具类对象
  ##不适合交给容器进行管理的bean
  封装实体类的域对象
3.4:常见报错
//原因:没有命名为‘ss2’的bean
//方法;检查bean容器别名和自己书写哪里不一致
NoSuchBeanDefinitionException: No bean named 'ss2' available

4:bean的实例化的三种方式

4.1:构造方法(常用)
	##提供可访问的构造方法:无参(bean容器创建对象调用无参构造方法)
    ##如果没有无参构造方法将报‘BeanCreationException’,‘java.lang.NoSuchMethodException’异常
4.2:Spring报错处理方法
#从后往前看
4.3:静态工厂(了解)
//静态工厂创建对象
public class OrderDaoFactory {
    public static OrderDao getOrderDao(){
        System.out.println("factory setup....");
        return new OrderDaoImpl();
    }
}
<!--    方式二:使用静态工厂实例化bean,记得配置工厂造对象的具体方法-->
<bean id="orderDao" class="com.ysj.factory.OrderDaoFactory" factory-method="getOrderDao"/>
4.4:实例工厂
4.4.1:实例工厂实例化(了解)
  • 第一个bean仅仅是为了配合使用,创造对象,除此之外无其他意义
  • factory-method:如果这样配置,方法名不固定,每次配置bean都要配置
<!--    方式三:使用实例工厂实例化bean-->
   <bean id="userFactory" class="com.ysj.factory.UserDaoFactory"/>
   <!--factory-bean:配置工厂类的bean-->
    <bean id="userDao" factory-method="getUserDao" factory-bean="userFactory"/>
4.4.2:FactoryBean(实用)
//FactoryBean创建对象
public class UserDaoFactoryBean implements FactoryBean<UserDao> {
    //代替原始实例工厂中创建对象的方法,bean中不必再配置factory-method,统一使用该方法创建对象
    public UserDao getObject() throws Exception {
        return new UserDaoImpl();
    }

    public Class<?> getObjectType() {
        return UserDao.class;
    }
    //true:创建对象是单例
    //false:创建对象非单例
    public boolean isSingleton() {
        return false;
    }
}
<!--方式四:使用FactoryBean实例化bean-->
<bean id="userDao" class="com.ysj.factory.UserDaoFactoryBean"/>

5:bean的生命周期

5.1:bean的初始化和销毁方法的配置
public class BookDaoImpl implements BookDao {
    public void save() {
        System.out.println("book dao save ...");
    }
    //表示bean初始化对应的操作
    public void init(){
        System.out.println("init...");
    }
    //表示bean销毁前对应的操作
    public void destory(){
        System.out.println("destroy...");
    }

}
<!--init-method:设置bean初始化生命周期回调函数-->
<!--destroy-method:设置bean销毁生命周期回调函数,仅适用于单例对象-->
<bean id="bookDao" class="com.ysj.dao.impl.BookDaoImpl" init-method="init" destroy-method="destory"/>

配置完后运行程序,会发现destroy方法没有运行,这时你大叫:“Why?”

其实是因为:我们这些程序运行在虚拟机上,当IOC容器加载完配置后,bean也初始化了,接着运行程序,

​ 然而当程序运行完后,虚拟机退出了,俺类destroy并没有机会执行,呜呜呜呜~~

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

    BookDao bookDao = (BookDao) ctx.getBean("bookDao");
    bookDao.save();
}

那么,如何让俺滴销毁方法执行呢?

有如下两个方法:

5.2:让销毁执行起来

注意把在加载IOC容器时定义ClassPathXmlApplicationContext对象

5.2.1:手动close(太暴力了,直接就停了)

用这个要注意使用的位置

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

    BookDao bookDao = (BookDao) ctx.getBean("bookDao");
    bookDao.save();
    //关闭容器
    ctx.close();
}
5.2.3:注册关闭钩子函数

​ 虚拟机会在退出之前调用此函数,进而关闭容器

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

    BookDao bookDao = (BookDao) ctx.getBean("bookDao");
    bookDao.save();
    //注册关闭钩子函数,在虚拟机退出之前回调此函数,关闭容器
    ctx.registerShutdownHook();

}
5.2:接口控制(了解)

按照Spring官方给的接口和规范进行配置

实现InitializingBean, DisposableBean接口

public class BookServiceImpl implements BookService, InitializingBean, DisposableBean {
    private BookDao bookDao;
    //在资源加载完后运行,但是在调用方法之前
    public void afterPropertiesSet() throws Exception {
        System.out.println("service init");
    }
    public void setBookDao(BookDao bookDao) {
        System.out.println("set .....");
        this.bookDao = bookDao;
    }

    public void save() {
        System.out.println("book service save ...");
        bookDao.save();
    }

    public void destroy() throws Exception {
        System.out.println("service destroy");
    }
}
5.3:生命周期历程
  • 初始化容器

    1. 创建对象(内存分配,类似于new,在内存中分配了空间)
    2. 执行构造方法(对象创建完成)
    3. 执行属性注入(set操作)
    4. 执行bean的初始化方法
  • 使用bean

    执行业务层

  • 关闭/销毁容器

    执行bean的销毁方法

5.4:小记

所有bean在IOC容器加载完配置后就被初始化了

4:依赖注入

4.1:setter注入

<!--注入简单类型-->
<bean id="bookDao" class="com.ysj.dao.impl.BookDaoImpl">
    <!--property标签:设置注入属性-->
    <!--name属性:设置注入的属性名,实际是set方法对应的名称-->
    <!--value属性:设置注入简单类型数据值-->
    <property name="connectionNum" value="100"/>
    <property name="databaseName" value="mysql"/>
</bean>
<!--注入引用类型-->
<bean id="bookService" class="com.ysj.service.impl.BookServiceImpl">
    <!--property标签:设置注入属性-->
    <!--name属性:设置注入的属性名,实际是set方法对应的名称-->
    <!--ref属性:设置注入引用类型bean的id或name-->
    <property name="bookDao" ref="bookDao"/>
    <property name="userDao" ref="userDao"/>
</bean>

4.2🥊构造器注入

<!--    标准书写-->
    <bean id="bookDao" class="com.ysj.dao.impl.BookDaoImpl">
<!--        根据构造方法参数名称注入-->
        <constructor-arg name="connectionNum" value="10"/>
        <constructor-arg name="databaseName" value="mysql"/>
    </bean>
    <bean id="userDao" class="com.ysj.dao.impl.UserDaoImpl"/>

    <bean id="bookService" class="com.ysj.service.impl.BookServiceImpl">
        <constructor-arg name="userDao" ref="userDao"/>
        <constructor-arg name="bookDao" ref="bookDao"/>
    </bean>
<!--    解决形参名称的问题,与形参名不耦合(了解)-->
    <bean id="bookDao" class="com.ysj.dao.impl.BookDaoImpl">
<!--        根据构造方法参数类型注入-->
        <constructor-arg type="int" value="10"/>
        <constructor-arg type="java.lang.String" value="mysql"/>
    </bean>
    <bean id="userDao" class="com.ysj.dao.impl.UserDaoImpl"/>

    <bean id="bookService" class="com.ysj.service.impl.BookServiceImpl">
        <constructor-arg name="userDao" ref="userDao"/>
        <constructor-arg name="bookDao" ref="bookDao"/>
    </bean>
<!--    解决参数类型重复问题,使用位置解决参数匹配-->
    <bean id="bookDao" class="com.ysj.dao.impl.BookDaoImpl">
        <!--根据构造方法参数位置注入-->
        <constructor-arg index="0" value="mysql"/>
        <constructor-arg index="1" value="100"/>
    </bean>
    <bean id="userDao" class="com.ysj.dao.impl.UserDaoImpl"/>

    <bean id="bookService" class="com.ysj.service.impl.BookServiceImpl">
        <constructor-arg name="userDao" ref="userDao"/>
        <constructor-arg name="bookDao" ref="bookDao"/>
    </bean>

4.3:依赖注入方式选择

1:铭记一点:推荐使用setter注入
2:Spring官方和第三返回给机构使用构造器是为了严谨
3:看具体项目,只有构造器注入的话也只能用了

在这里插入图片描述

4.4:自动装配

  • byType​:按类型装配(容器中相同类型的bean必须唯一,推荐推荐)
  • byName:按名称装配(容器中要有相应名称的bean,因为变量名与配置的名称耦合,不推荐)
  • 自动装配适用于引用类型的注入,并不能对简单类型进行操作
  • 自动装配的优先级低于setter注入和构造器注入,同时有可能出现自动装配失效
 <!--这个id="bookDao"可以不写-->
<bean id="bookDao" class="com.ysj.dao.impl.BookDaoImpl"/>
<!--autowire属性:开启自动装配,通常使用按类型装配-->
<bean id="bookService" class="com.ysj.service.impl.BookServiceImpl" autowire="byType"/>

4.5:集合注入

着重记下map

<bean id="bookDao" class="com.ysj.dao.impl.BookDaoImpl">
    <!--数组注入-->
    <property name="array">
        <array>
            <value>100</value>
            <value>200</value>
            <value>300</value>
        </array>
    </property>
    <!--list集合注入-->
    <property name="list">
        <list>
            <value>itcast</value>
            <value>ysj</value>
            <value>boxuegu</value>
            <value>chuanzhihui</value>
        </list>
    </property>
    <!--set集合注入-->
    <property name="set">
        <set>
            <value>itcast</value>
            <value>ysj</value>
            <value>boxuegu</value>
            <value>boxuegu</value>
        </set>
    </property>
    <!--map集合注入(重点)-->
    <property name="map">
        <map>
            <entry key="country" value="china"/>
            <entry key="province" value="henan"/>
            <entry key="city" value="kaifeng"/>
        </map>
    </property>
    <!--Properties注入-->
    <property name="properties">
        <props>
            <prop key="country">china</prop>
            <prop key="province">henan</prop>
            <prop key="city">kaifeng</prop>
        </props>
    </property>
</bean>

5:案例:数据源对象管理

5.1:德鲁伊连接池管理(Druid)

​ 第一步:先把相关的依赖导入

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

<!-- 导入Druid的坐标-->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.1.16</version>
    </dependency>

    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.45</version>
    </dependency>

第二部:现在也没啥事儿,利用Spring新建个bean对象测试呗

ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");

DataSource dataSource = (DataSource) ctx.getBean("dataSource");
System.out.println(dataSource)

5.2:c3p0连接池配置

第一步:同Druid

<!-- c3p0的配置,这里只做了基本的配置,其他的类似最大连接数的可自行配置-->
<bean id="dataSource"  class="com.mchange.v2.c3p0.ComboPooledDataSource">
    <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
    <property name="jdbcUrl" value="dbc:mysql://localhost:3308/mybatis"></property>
    <property name="user" value="root"></property>
    <property name="password" value="yangshijie"></property>

</bean>

第二步:同druid

5.3:加载properties配置文件

上面那样写在配置文件里写死真的好吗?

接下来我们在配置文件里进行配置

  • 第一步:开启context命名空间
<context:property-placeholder location="jdbc.properties"/>
 xsi:schemaLocation="
            http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd
            "
  • 第二步:使用context命名空间加载properties文件
<context:property-placeholder location="jdbc.properties"/>
  • 小记:properties文件里可能有与系统一样的变量名,设置system-properties-mode=“NEVER”,该变量才能被加载。但是现在这个location="jdbc.properties"也只能加载一个文件呀
<context:property-placeholder location="jdbc.properties" system-properties-mode="NEVER"/>
  • 下面施加在多个文件的,location="jdbc.properties,jdbc2.properties"中间价格逗号就可
<context:property-placeholder location="jdbc.properties,jdbc2.properties" system-properties-mode="NEVER"/>
  • 直接加载全部配置文件 location="*.properties,jdbc2.properties",但是呢,咱们这样写不太专业
<context:property-placeholder location="*.properties,jdbc2.properties" system-properties-mode="NEVER"/>
  • 标准格式(但是它只能读取模块内的配置文件,一些第三方打包好的jar包里的咱们扫描不到)

    <context:property-placeholder location="classpath:*.properties" system-properties-mode="NEVER"/>
    
  • 下面是最标注的格式,都能读location="classpath*:*.properties"

    <context:property-placeholder location="classpath*:*.properties" system-properties-mode="NEVER"/>
    

6:容器

6.1:创建容器

在这里插入图片描述

6.2:获取bean

  • 通过bean的名称获取,但是有强转(说实话,不太好)
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
  • 先通过bean名称,在找到BookDao.class对应的类创建对象
BookDao bookDao = ctx.getBean("bookDao",BookDao.class);
  • 直接通过类型创建,但是如果对应BookDao.class的bean有多个,那这一下就创建出来很多对象了。
BookDao bookDao = ctx.getBean(BookDao.class);

6.3:beanFactory加载和ApplicationContext加载的区别

  • beanFactory:所有bean延迟加载(在加载配置文件时,没有创建对象,即没有执行构造方法)

  • ApplicationContext:立即加载

    ​ 但是可以在配置bean的时候加上lazy-init="true"实现延迟加载的效果

    <bean id="bookDao" class="com.ysj.dao.impl.BookDaoImpl" lazy-init="true"/>
    

7:注解开发

7.1:配置bean

  • 直接创建一个Spring配置类
    • @Configuration:声明当前类为Spring配置类
    • @ComponentScan:设置bean扫描路径
    • @Component:定义当前类为bean
      • ​ @Service:业务层
      • ​ @Repository:表现层
      • ​ @Controller:控制层
//声明当前类为Spring配置类
@Configuration
//设置bean扫描路径,多个路径书写为字符串数组格式
@ComponentScan({"com.ysj.service","com.ysj.dao"})
public class SpringConfig {
}
  • 改为配置类后,初始化容器对象就切换到读取Java配置类

    //AnnotationConfigApplicationContext加载Spring配置类初始化Spring容器
    ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
    

7.2:管理bean

@Scope("singleton")  //配置该bean初始化对象是单例
@Scope("prototype")	 //多例
@PostConstruct   //配置初始化方法
@PreDestroy		 //配置销毁方法

7.3:依赖注入

@Autowired  //自动注入,适用于引用类型,默认按类型装配
@Qualifier//依赖于@Autowired,按bean名称进行自动装配
@Value//适用于简单类型注入,@Value("${name}")可通过EL表达式获取配置文件里的属性值
@PropertySource({"jdbc.properties","jdbc2.properties"})
    //加载配置文件,如果有多个,用大括号圈起来
    //不允许使用通配符*
7.3.2:管理第三方bean
//1:定义一个方法获得要管理的对象
@Bean
public DataSource dataSource(){
    DruidDataSource druidDataSource = new DruidDataSource();
    druidDataSource.setDriverClassName("com.mysql.jdbc.Driver");
    druidDataSource.setUrl("jdbc:mysql://localhost:3306/mybatis");
    druidDataSource.setUsername("root");
    druidDataSource.setPassword("root");
    System.out.println(druidDataSource);
    return druidDataSource;
}
  • 记得使用独立的配置类管理第三方bean

  • 扫描式:给非Spring配置类也加上@Configuration让其变成配置类,接着在Spring配置类进行包扫描即可(不推荐,因为不方便读)

  • 导入式:在Spring配置类中Import类(无需将该类变为配置类,推荐)

    //@Import:导入配置信息
    @Import({JdbcConfig.class})
    
  • 为第三方bean注入资源

    简单类型:简单粗暴,直接定义变量,赋值,然后再方法里用

    @Value("com.mysql.jdbc.Driver")
    private String driver;
    @Value("jdbc:mysql://localhost:3306/spring_db")
    private String url;
    @Value("root")
    private String userName;
    @Value("root")
    private String password;
    //2.添加@Bean,表示当前方法的返回值是一个bean
    //@Bean修饰的方法,形参根据类型自动装配
    @Bean
    public DataSource dataSource(BookDao bookDao){
        System.out.println(bookDao);
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName(driver);
        ds.setUrl(url);
        ds.setUsername(userName);
        ds.setPassword(password);
        return ds;
    }
    

    假设我们一个bean中还需要使用第三方bean,怎么办?往下看啊!

  • 引用类型:先在Spring配置类中加上包扫描(确保能扫描到要使用的第三方bean),然后直接在要使用第三方bean的方法中,加上该类bean类型的形参,Spring容器会根据形参类型自动装配bean。

    //2.添加@Bean,表示当前方法的返回值是一个bean
    //@Bean修饰的方法,形参根据类型自动装配
    @Bean
    public DataSource dataSource(BookDao bookDao){
        System.out.println(bookDao);
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName(driver);
        ds.setUrl(url);
        ds.setUsername(userName);
        ds.setPassword(password);
        return ds;
    }
    

7.4:Spring整合mybatis

  • 导入相关依赖
<dependency>
  <groupId>org.mybatis</groupId>
  <artifactId>mybatis</artifactId>
  <version>3.5.6</version>
</dependency>

<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <version>5.1.47</version>
</dependency>

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-jdbc</artifactId>
  <version>5.2.10.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.mybatis</groupId>
  <artifactId>mybatis-spring</artifactId>
  <version>1.3.0</version>
</dependency>
public class MybatisConfig {

    @Bean
    public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){
        //1:先造出SqlSessionFactoryBean的对象
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        //2:设置类型对象所在包
        sqlSessionFactoryBean.setTypeAliasesPackage("com.ysj.domain");
        //3:设置数据源
        sqlSessionFactoryBean.setDataSource(dataSource);

        return sqlSessionFactoryBean;
    }
    //4:设置sql文件所在报的扫描路径
    @Bean
    public MapperScannerConfigurer mapperScannerConfigurer(){
        MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
        mapperScannerConfigurer.setBasePackage("com.ysj.dao");
        return  mapperScannerConfigurer;
    }
}

最后最后,千万别忘了把mybatis配置文件导入Spring配置类中

@Import({JdbcConfig.class,MybatisConfig.class})

7.5:Spring整合Junit

导入依赖

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

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-test</artifactId>
  <version>5.2.10.RELEASE</version>
</dependency>
//设置类运行器
@RunWith(SpringJUnit4ClassRunner.class)
//设置Spring环境对应的配置类
@ContextConfiguration(classes = SpringConfig.class)
//支持自动装配注入bean,按类型查找
@Autowired
private AccountService accountService;

@Test
public void testFindById(){
    System.out.println(accountService.findById(1));

}

8:AOP

8.1:入门案例

  • 导入相关依赖,AOP的依赖在Spring-context中已经被包含了,所以只需要再导入切面依赖即可

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.2.10.RELEASE</version>
    </dependency>
    <!--切面依赖-->
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>1.9.4</version>
    </dependency>
    
  • 自定义一个通知类(通知类必须配置成Spring管理的bean)

  • 设置通知类为切面类

  • 在Spring配置类中开启注解开发AOP功能

  • 设置要作为增强功能的方法和切入点

//通知类必须配置成Spring管理的bean
@Component
//设置当前类为切面类类
@Aspect
public class MyAdvice {
    //设置切入点,要求配置在方法上方
    @Pointcut("execution(void com.ysj.dao.BookDao.update())")
    private void pt(){}

    //设置在切入点pt()的前面运行当前操作(前置通知)
    // @Before("pt()")
    public void method(){
        System.out.println(System.currentTimeMillis());
    }
}

8.2:AOP工作流程

  1. Spring容器启动
  2. 读取页面所有的切入点
  3. 初始化bean,判断bean中是否有方法匹配到任意切入点
    • 匹配到,创建代理对象
    • 未匹配到,创建初始的对象
  4. 获取bean的执行方法
    • 获取bean,调用方法执行,完成操作
    • 获取的bean是代理对象时,执行是按照代理对象的运行方式运行原生方法和AOP定义的增强方法

Eg:在AOP中,AOP对最终的对象的ToString方法进行了重写,如果直接输出对象则是类似下面这样

`com.ysj.dao.impl.BookDaoImpl@4988d8b8`

8.3:AOP切入点表达式

  • *** ** :单个独立的任意符号,可独自出现,也可作为前缀或后缀的匹配符出现

  • :多个连续的任意符号,可独立出现,用于简化包名与参数的书写

  • + :专门用于匹配子类类型

    //表示:任意返回值类型,在为顶层的包结构下任意包中的以Dao结尾的接口或类的子类中以up开头的无参方法
    @Pointcut("execution(* com.*..*Dao+.up*())")
    

在这里插入图片描述

8.4:AOP通知类型

  • @before(“原始方法”):在原始方法运行之前

  • @after():在原始方法运行之后

  • @Around():环绕原始方法运行,可定义位置

    • 环绕通知必须依赖形参ProceedingJoinPoint才能在方法内部对原始方法进行调用

    • 未使用ProceedingJoinPoint将跳过原始方法的执行

    • 对原始方法接收返回值的话必须设置返回值类型为Object,不接收的话可void,也可Object

      public Object around(ProceedingJoinPoint pjp)
      
    • 因为无法预知原始方法是否抛出异常,所以环绕通知必须抛出Throwable异常

      public Object around(ProceedingJoinPoint pjp) throws Throwable {
          System.out.println("around before advice ...");
          //表示对原始操作的调用
          Object ret = pjp.proceed();
          System.out.println("around after advice ...");
          return ret;  //返回原始方法执行的返回值
      }
      
  • 另外俩(了解):

        //@AfterReturning:返回后通知,在原始方法执行完毕后运行,且原始方法执行过程中未出现异常现象
    //    @AfterReturning("pt2()")
        public void afterReturning() {
            System.out.println("afterReturning advice ...");
        }
    
        //@AfterThrowing:抛出异常后通知,在原始方法执行过程中出现异常后运行
        @AfterThrowing("pt2()")
        public void afterThrowing() {
            System.out.println("afterThrowing advice ...");
        }
    

8.5:测试业务层接口执行效率

//设置环绕通知,在原始操作的运行前后记录执行时间
@Around("ProjectAdvice.servicePt()")
public void runSpeed(ProceedingJoinPoint pjp) throws Throwable {
    //获取执行的签名对象
    Signature signature = pjp.getSignature();
    //获取类类型(带路径)
    String className = signature.getDeclaringTypeName();
    //获取类名
    String methodName = signature.getName();

    long start = System.currentTimeMillis();
    for (int i = 0; i < 10000; i++) {
       pjp.proceed();
    }
    long end = System.currentTimeMillis();
    System.out.println("万次执行:"+ className+"."+methodName+"---->" +(end-start) + "ms");
}

8.6:通知获取数据

  • ProceedingJonPoint:专用于环绕通知,是JoinPoint子类,可以实现对原始方法的调用

  • JoinPoint:用来描述切入点对象,但必须配置成通知方法中的第一个参数,可以实现对原始方法的调用

  • getArgs():用来获取原始方法中的参数,返回值是一个数组
    
  • 在原始方法return后和抛出异常后

    //设置返回后通知获取原始方法的返回值,要求returning属性值必须与方法形参名相同
    @AfterReturning(value = "pt()",returning = "ret")
    public void afterReturning(JoinPoint jp,String ret) {
        System.out.println("afterReturning advice ..."+ret);
    }
    
    //设置抛出异常后通知获取原始方法运行时抛出的异常对象,要求throwing属性值必须与方法形参名相同
    @AfterThrowing(value = "pt()",throwing = "t")
    public void afterThrowing(Throwable t) {
        System.out.println("afterThrowing advice ..."+t);
    }
    

9:Spring事务

  • 阳间小技巧:想想数据库事务

9.1:准备工作

  • 在Spring篇配置类中注册事务驱动
    @EnableTransactionManagement
    
  • 在jdbc配置类中配置事务管理器

    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource){
        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
        transactionManager.setDataSource(dataSource);
        return transactionManager;
    }
    
  • 给相关需要事务管理的接口方法配置事务

    //配置当前接口方法具有事务
    @Transactional
    public void transfer(String out,String in ,Double money) ;
    
  • 开始测试

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(classes = SpringConfig.class)
    public class AccountServiceTest {
    
        @Autowired
        private AccountService accountService;
    
        @Test
        public void testTransfer() throws IOException {
                accountService.transfer("Tom","Jerry",100D);
        }
    
    }
    

9.2:事务传播行为

  • 当你在一个测试中测试两个两个甚至是多个事务,但是其中一个事务是必须实现的(无论程序是否发生错误),这个时候怎么办呢,只需要给这个事务的接口配置事务属性propagation = Propagation.REQUIRES_NEW就行了
//propagation设置事务属性:传播行为设置为当前操作需要新事务
@Transactional(propagation = Propagation.REQUIRES_NEW)
void log(String out, String in, Double money);
  • 怎么设置事务遇到异常回滚呢?别慌,看我操作。

    //下面设置的是,当事务遇到IO异常,就事务回滚。    
    //rollback:设置当前事务参与回滚的异常,默认非运行时异常不参与回滚
       @Transactional(rollbackFor = IOException.class)
    
评论 29
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Duck&踏风彡

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值