一、Spring初识:

什么是Spring?

spring是分层的JavaSE/EE应用full-stack(全栈)轻量级框架,以ioc(Inverse of control ) 和Aop(aspect oriendprogramming)为内核,提供了表现层springMvc 和持久层springJDBCREmplate 以及业务层事务管理等众多企业级应用技术,还整合了开源世界众多的第三方框架和类库,逐渐成为使用最多的javaEE企业级开源框架。
发展历程:
1997年,IBM、公司提供EJB思想
1998年,SUM指定开发标准规范EJB 1.0
1999年,EJB1.1 发布
2001年,EJB2.0 发布
2003年,EJB2.1发布
2006年,EJB3.0 发布

spring优势:

1)结构方便,简化开发
通过spring提供的IOC容器,可以将对象依赖关系由Spring进行控制,避免硬解码所造成的过度耦合。用户也不必为单列模式类,属性文件解析等这些很底层的需求写代码,可以更专注上层应用。

2)AOP编程的支持
通过Spring的AOP功能,方便进行面向切面编程,许多不容易用传统OOP实现的功能可以通过AOP轻松实现。
3)声明式事务的支持
可以将我们从单调烦闷的事务管理中解脱出来,通过声明式灵活的进行事务管理,提高开发效率。
4)方便程序测试
可以用非容器依赖的编程方式进行所有的测试工作,测试不再是昂贵的操作,而是随手可做的事情。
5)方便集成各种优秀框架
Spring对各种优秀框架。如Structs、Hibernate、Quartz、Mybatis、SpringMvc、Redis….都支持。
6)降低JavaEE的 API使用难度
Spring 对javaEE API 如:JDBC,JavaMail,远程调用等。。。进行了薄薄的封装,使用这些Api让使用难度大大降低。
7)经典学习范例
spring的源代码设计非常精妙,结构清晰,匠心打造,处处体现了大师对Java设计模式的灵活运用以及对Java技术高深的造诣。

Spring的体系结构:

Spring笔记: - 图1

二、spring的基本使用

1.Spring maven版本依赖:

  1. <!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
  2. <dependency>
  3. <groupId>org.springframework</groupId>
  4. <artifactId>spring-webmvc</artifactId>
  5. <version>5.2.0.RELEASE</version>
  6. </dependency>
  7. <!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
  8. <dependency>
  9. <groupId>org.springframework</groupId>
  10. <artifactId>spring-jdbc</artifactId>
  11. <version>5.2.0.RELEASE</version>
  12. </dependency>
  13. <!-- spring -->
  14. <dependency>
  15. <groupId>org.springframework</groupId>
  16. <artifactId>spring-context</artifactId>
  17. <version>5.3.9.RELEASE</version>
  18. </dependency>

2.编写一个Hello实体类

  1. public class Hello {
  2. public Hello(){
  3. System.out.println("我创建了" );
  4. }
  5. private String name;
  6. public String getName() {
  7. return name;
  8. }
  9. public void setName(String name) {
  10. this.name = name;
  11. }
  12. public void show(){
  13. System.out.println("Hello,"+ name );
  14. }
  15. }

3.编写我们的spring文件 , 这里我们命名为beans.xml

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://www.springframework.org/schema/beans
  5. http://www.springframework.org/schema/beans/spring-beans.xsd">
  6. <!--bean就是java对象 , 由Spring创建和管理-->
  7. <!--
  8. - id 获取bin的唯一标识符
  9. -class 对象的包全名
  10. - scope="singleton"代表单例模式(bean实例化一个)
  11. - scope="prototype"代表多例模式(bean多个)
  12. - 当容器创建时就 创建对象
  13. -->
  14. <bean id="hello" class="com.kuang.pojo.Hello" scope="singleton">
  15. <property name="name" value="Spring"/>
  16. </bean>
  17. </beans>

4.进行测试

@Test
public void test(){
   //解析beans.xml文件 , 生成管理相应的Bean对象
   ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
   //getBean : 参数即为spring配置文件中bean的id .
   Hello hello = (Hello) context.getBean("hello");
   hello.show();
}

2》. Bean生命周期:

2.1 单scope=”singleton” 时(默认)单例模式:

Bean实例化个数:1个
Bean实例的时机:当容器创建时,对象就会被创建

  1. 创建:当应用加载,创建容器时,对象被创建
  2. 运行:只要容器在,对象一直活着
  3. 销毁:当应用卸载时,摧毁容器时,对象被销毁了。

2.2 单scope=”prototype” 时(默认)多例模式:

Bean实例化个数:多个
Bean实例的时机:当调用getBean()方法时,对象就会被创建

  1. 创建:当使用对象时,创建新的对象实例
  2. 运行:对象使用中,对象就一直活着
  3. 销毁:长时间不用就会被垃圾回收机制回收

    3》Bean实例化的三种方式:

    3.1无参构造方法实例化:

    ```xml

<?xml version=”1.0” encoding=”UTF-8”?>

```java //加载配置文件 ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml"); //获取Bean对象 IUserLoginServiceImpl userLoginService = (IUserLoginServiceImpl)app.getBean("userLoginService"); //获取Bean对象 IUserLoginServiceImpl userLoginService1 = (IUserLoginServiceImpl)app.getBean("userLoginService"); ### 3.2工厂静态方法实例化: applicationContext.xml文件配置: java <!-- 工厂静态方法实例化--> <bean id="userLoginService" class="com.xxgc.spring.factory.StaticFactory" factory-method="getUserLoginService" /> 工厂实例化: java package com.xxgc.spring.factory; import com.xxgc.spring.service.impl.IUserLoginServiceImpl; /** * 工厂静态方法实例化 */ public class StaticFactory { /** * 定义一个方法用来返回一个IUserLoginServiceImpl对象 */ public static IUserLoginServiceImpl getUserLoginService(){ System.out.println("工厂生产中"); return new IUserLoginServiceImpl(); } } 获取Bean: java @Test public void test1(){ //加载applicationContext.xml文件 ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml"); //获取Bean对象 IUserLoginServiceImpl userLoginService = (IUserLoginServiceImpl)app.getBean("userLoginService"); System.out.println("userLoginService = " + userLoginService); userLoginService.userLogin("张三","333"); } ### 3.3工厂实例方法实例化: applicationcontext.xml配置文件: xml <!-- 工厂实例方法实例化--> <!--创建工厂 factory --> <bean id="factory" class="com.xxgc.spring.factory.DynamicFactory"/> <bean id="userLoginService" factory-bean="factory" factory-method="getUserLoginService"/> 工厂类: java package com.xxgc.spring.factory; import com.xxgc.spring.service.impl.IUserLoginServiceImpl; /** * 工厂实例方法实例化 */ public class DynamicFactory { /** * 定义一个方法用来返回一个IUserLoginServiceImpl对象 */ public IUserLoginServiceImpl getUserLoginService(){ System.out.println("工厂实例方法实例化"); return new IUserLoginServiceImpl(); } } 获取Bean: java @Test public void test1(){ //加载applicationContext.xml文件 ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml"); //获取Bean对象 IUserLoginServiceImpl userLoginService = (IUserLoginServiceImpl)app.getBean("userLoginService"); System.out.println("userLoginService = " + userLoginService); userLoginService.userLogin("张三","333"); } ## 4》Bean的依赖注入概念: 依赖注入(Dependency Injection) ,它是 Spring框架的核心IOC的具体实现。
在编写程序时,通过控制反转,把对象的创建交给Spring,但代码中不可能没有依赖情况,IOC解耦只是降低了他们的依赖关系,但不会清除掉依赖。 ## 5》Bean的注入方式: ### 5.1 set方法注入: applicationcontext.xml配置文件: xml <!-- - id 获取bin的唯一标识符 -class 对象的包全名 - scope="singleton"代表单例模式(bean实例化一个) - scope="prototype"代表多例模式(bean多个) - 当容器创建时就 创建对象 --> <!-- 无参构造方法--> <!--UserDaoMapper Bean --> <bean id="userDao" class="com.xxgc.spring.dao.impl.UserDaoMapperImpl"/> <bean id="userLoginService" class="com.xxgc.spring.service.impl.IUserLoginServiceImpl" scope="singleton"> <!--set方法注入 UserDaoMapper--> <property name="udm" ref="userDao"></property> </bean> 定义set方法: java package com.xxgc.spring.service.impl; import com.xxgc.spring.dao.UserDaoMapper; import com.xxgc.spring.dao.impl.UserDaoMapperImpl; import com.xxgc.spring.service.IUserLoginService; import org.springframework.stereotype.Service; /** * 用户登录模块接口实现 */ @Service public class IUserLoginServiceImpl implements IUserLoginService { //和dao层建立关系(通道) private UserDaoMapper udm; //set方法注入 public void setUdm(UserDaoMapper udm) { this.udm = udm; } public int userLogin(String uName, String uPass) { //调用dao的方法 udm.selectUserById(); if("张三".equals(uName) &&"333".equals(uPass)){ System.out.println("登录成功"); }else{ System.out.println("对不起,您的用户名或密码错误"); } return 0; } public int userRegister(String uName, String uPass) { System.out.println("用户注册!!!"); return 0; } } 测试: java @Test public void test1(){ //加载applicationContext.xml文件 ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml"); //获取Bean对象 IUserLoginServiceImpl userLoginService = (IUserLoginServiceImpl)app.getBean("userLoginService"); System.out.println("userLoginService = " + userLoginService); userLoginService.userLogin("张三","333"); } ### 5.2 构造方法注入: applicationcontext.xml配置文件: xml <!-- - id 获取bin的唯一标识符 -class 对象的包全名 - scope="singleton"代表单例模式(bean实例化一个) - scope="prototype"代表多例模式(bean多个) - 当容器创建时就 创建对象 --> <!-- 无参构造方法--> <!--UserDaoMapper Bean --> <bean id="userDao" class="com.xxgc.spring.dao.impl.UserDaoMapperImpl"/> <bean id="userLoginService" class="com.xxgc.spring.service.impl.IUserLoginServiceImpl" scope="singleton"> <!-- 构造方法注入--> <constructor-arg name="udm" ref="userDao"></constructor-arg> </bean> 定义构造方法方法: java package com.xxgc.spring.service.impl; import com.xxgc.spring.dao.UserDaoMapper; import com.xxgc.spring.dao.impl.UserDaoMapperImpl; import com.xxgc.spring.service.IUserLoginService; import org.springframework.stereotype.Service; /** * 用户登录模块接口实现 */ @Service public class IUserLoginServiceImpl implements IUserLoginService { //和dao层建立关系(通道) private UserDaoMapper udm; /** *构造方法注入 */ public IUserLoginServiceImpl(UserDaoMapper udm){ this.udm = udm; } public IUserLoginServiceImpl() { } public int userLogin(String uName, String uPass) { //调用dao的方法 udm.selectUserById(); if("张三".equals(uName) &&"333".equals(uPass)){ System.out.println("登录成功"); }else{ System.out.println("对不起,您的用户名或密码错误"); } return 0; } public int userRegister(String uName, String uPass) { System.out.println("用户注册!!!"); return 0; } } 测试: java @Test public void test1(){ //加载applicationContext.xml文件 ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml"); //获取Bean对象 IUserLoginServiceImpl userLoginService = (IUserLoginServiceImpl)app.getBean("userLoginService"); System.out.println("userLoginService = " + userLoginService); userLoginService.userLogin("张三","333"); } ## 依赖注入ref的使用(无参创建): ```xml <?xml version=”1.0” encoding=”UTF-8”?>
<a name="Y96tV"></a>
## 通过有参构造方法来创建

```xml
<!-- 第一种根据index参数下标设置 -->
<bean id="userT" class="com.kuang.pojo.UserT">
   <!-- index指构造方法 , 下标从0开始 -->
   <constructor-arg index="0" value="kuangshen2"/>
</bean>
<!-- 第二种根据参数名字设置 -->
<bean id="userT" class="com.kuang.pojo.UserT">
   <!-- name指参数名 -->
   <constructor-arg name="name" value="kuangshen2"/>
</bean>
<!-- 第三种根据参数类型设置 -->
<bean id="userT" class="com.kuang.pojo.UserT">
   <constructor-arg type="java.lang.String" value="kuangshen2"/>
</bean>

1.1.Bean的配置

<!--bean就是java对象,由Spring创建和管理-->

<!--
   id 是bean的标识符,要唯一,如果没有配置id,name就是默认标识符
   如果配置id,又配置了name,那么name是别名
   name可以设置多个别名,可以用逗号,分号,空格隔开
   如果不配置id和name,可以根据applicationContext.getBean(.class)获取对象;

class是bean的全限定名=包名+类名
-->
<bean id="hello" name="hello2 h2,h3;h4" class="com.kuang.pojo.Hello">
   <property name="name" value="Spring"/>
</bean>

1.2import

团队的合作通过import来实现 .
在主配置文件中导入user的配置:
image.png

<import resource="{path}/beans.xml"/>

<!--或者-->
<!--导入user模块-->
<import resource="applicationContext-user.xml"/>

6》扩展的注入的几种方式:(重点)

1、常量注入

package com.xxgc.spring.dao.impl;

import com.xxgc.spring.dao.UserDaoMapper;

public class UserDaoMapperImpl implements UserDaoMapper {
    //属性
    private int age;
    //set方法
    public void setAge(int age) {
        this.age = age;
    }

    public int selectUserById() {
        System.out.println("数据库查询到了张三"+age+"岁");
        return 0;
    }
}
 <bean id="student" class="com.kuang.pojo.Student">
     <property name="age" value="18"/>
 </bean>

2、Bean注入

 <bean id="addr" class="com.kuang.pojo.Address">
     <property name="address" value="重庆"/>
 </bean>

 <bean id="student" class="com.kuang.pojo.Student">
     <property name="name" value="小明"/>
     <property name="address" ref="addr"/>
 </bean>

3、数组注入

 <bean id="student" class="com.kuang.pojo.Student">
     <property name="name" value="小明"/>
     <property name="address" ref="addr"/>
     <property name="books">
         <array>
             <value>西游记</value>
             <value>红楼梦</value>
             <value>水浒传</value>
         </array>
     </property>
 </bean>

4、List注入

 //属性
private List<String> userList;
//set方法
 public void setUserList(List<String> userList) {
        this.userList = userList;
 }
<bean id="userDao" class="com.xxgc.spring.dao.impl.UserDaoMapperImpl">
  <property name="userList">
    <list>
      <value>苹果</value>
      <value>香蕉</value>
      <value>西瓜</value>
    </list>
  </property>
</bean>

5、Map注入

ctrl+alt+↓ :向下复制
itit:迭代器
alt+↓ :当前行向下移动

 //属性
private Map<String,String> usetMap;
//set方法
 public void setUsetMap(Map<String, String> usetMap) {
        this.usetMap = usetMap;
 }
<bean id="userDao" class="com.xxgc.spring.dao.impl.UserDaoMapperImpl">
  <property name="usetMap">
    <map>
      <entry key="zhangsan" value="张三"></entry>
      <entry key="lisi" value="李四"></entry>
      <entry key="wangwu" value="王五"></entry>
    </map>
  </property>
</bean>

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="学号">20190604</prop>
         <prop key="性别">男</prop>
         <prop key="姓名">小明</prop>
     </props>
 </property>

9、p命名和c命名注入

1、P命名空间注入 : 需要在头文件中加入约束文件

导入约束 : xmlns:p="http://www.springframework.org/schema/p"

 <!--P(属性: properties)命名空间 , 直接注入属性-->
 <bean id="user" class="com.kuang.pojo.User" p:name="狂神" p:age="18"/>

2、c 命名空间注入 : 需要在头文件中加入约束文件


导入约束 : xmlns:c="http://www.springframework.org/schema/c"
 <!--C(构造: Constructor)命名空间 , 使用构造器注入-->
 <bean id="user" class="com.kuang.pojo.User" c:name="狂神" c:age="18"/>

7》配置文件总结:

<bean id="" class="" scope="">标签 一个对应着一个对象
    id属性 容器的唯一标识符
    class属性  实例化Bean的全限定名
    scope属性 Bean的作用域,单例或多例
    <property name="" value="" ref="">标签  属性注入(set方法)
        name属性 注入名称(属性名)
        value属性  注入的普通属性值
        ref属性  注入对象的应引用值
        <list>
        <map>
        <properties>...
    <constructor-arg name="" value="" ref="">有参构造注入
 <import resource="">标签  导入其它分模块文件

三、Spring相关API:

1》ApplicationContext的实现:

1.1 ClassPathXmlApplicationContext:

作用域:从根路径加载配置文件(推荐使用)


//加载applicationContext.xml文件
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");

1.2 FileSystemXmlApplicationContext:

作用域:从磁盘上加载配置文件,可以配置在磁盘的任意位置

//加载applicationContext.xml文件
ApplicationContext app = new FileSystemXmlApplicationContext("C:\\SystemConfig\\applicationContext.xml");

1.3 AnnotationConfigApplicationContext:

作用域:当使用注解时,需要使用此类来创建Spring容器来读注解。


 //加载配置文件
 ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfiguration.class);

2》 Spring配置数据源:

2.1 什么是数据源?

数据源也称为数据库连接池,是用来提高程序性能的一个必不可少的技术。
1.事先实例化数据源。初始化数据源
2.使用资源时从数据源获取
3.使用完毕过后归还数据源

常见的数据源有:DBCP、C3P0(市面上最流行)、BoneCP、Druid(阿里巴巴集团)

开发步骤:

1.导入数据源坐标和数据驱动坐标
2.创建数据源对象
3.设置数据源的进本连接数据
4.使用数据源获取连接和归还连接

                <!--数据库驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.44</version>
        </dependency>
        <!-- c3p0 数据库连接池-->
        <dependency>
            <groupId>c3p0</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.1.2</version>
        </dependency>
        <!-- druid 数据库连接池-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.8</version>
        </dependency>

1.1c3p0:

原生态创建获取连接(c3p0):

    //数据源c3p0  更为稳定
    @Test
    public void c3p0() throws Exception {
        //实例化数据源并配置
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setDriverClass("com.mysql.jdbc.Driver");
        dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/mybatis?serverTimezone=GMT&amp;useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8");
        dataSource.setUser("root");
        dataSource.setPassword("123456");

        //获取连接
        Connection connection = dataSource.getConnection();
        System.out.println("connection = " + connection);
        //归还连接
        connection.close();
    }

c3p0(配置文件版本):
image.png
jdbc.properties 配置文件:

jdbc.test.driver=com.mysql.jdbc.Driver
jdbc.test.url=jdbc:mysql://localhost:3306/mybatis?serverTimezone=GMT&amp;useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8
jdbc.test.username=root
jdbc.test.password=123456
 //数据源c3p0(配置文件版本)  更为稳定
    @Test
    public void c3p01() throws Exception {
        //加载配置文件
        ResourceBundle rb = ResourceBundle.getBundle("jdbc");
        String driver = rb.getString("jdbc.test.driver");
        String url = rb.getString("jdbc.test.url");
        String username = rb.getString("jdbc.test.username");
        String password = rb.getString("jdbc.test.password");

        //实例化数据源并配置
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setDriverClass(driver);
        dataSource.setJdbcUrl(url);
        dataSource.setUser(username);
        dataSource.setPassword(password);

        //获取连接
        Connection connection = dataSource.getConnection();
        System.out.println("connection = " + connection);
        //归还连接
        connection.close();
    }

使用Spring配置c3p0数据源:

c3p0(配置文件版本):
image.png
jdbc.properties 配置文件:

jdbc.test.driver=com.mysql.jdbc.Driver
jdbc.test.url=jdbc:mysql://localhost:3306/mybatis?serverTimezone=GMT&amp;useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8
jdbc.test.username=root
jdbc.test.password=123456

applicationContext.xml配置文件:

   <?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context
                           https://www.springframework.org/schema/context/spring-context.xsd">

  <!-- 导入外部properties-->
  <context:property-placeholder location="jdbc.properties"/>
  <!--配置c3p0数据源 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/mybatis?serverTimezone=GMT&amp;useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8"></property>
        <property name="user" value="root"></property>
        <property name="password" value="123456"></property>
    </bean>
  </beans>

java获取连接池并使用:

 @Test
    public void test() throws SQLException {
        //加载配置文件
        ApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");

        //获取数据源对象
        ComboPooledDataSource dataSource = classPathXmlApplicationContext.getBean(ComboPooledDataSource.class);
        //获取连接
        Connection connection = dataSource.getConnection();
        System.out.println("connection = " + connection);
        //归还连接
        connection.close();
    }

1.2 Druid:

原生态创建获取连接(Druid):

 //Druid数据源 并发速度快
    @Test
    public void Druid() throws SQLException {
        //创建Druid数据源
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/mybatis?serverTimezone=GMT&amp;useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8");
        dataSource.setUsername("root");
        dataSource.setPassword("123456");
        //获取连接
        DruidPooledConnection connection = dataSource.getConnection();
        System.out.println("connection = " + connection);
        //归还连接
        connection.close();
    }

3》Spring的Ioc注解开发:

3.1Spring原始注解:

Spring是一个轻代码重配置的框架,配置比较繁重,严重影响开发效率,所以注解开发是一种趋势,直接代替xml配置文件可以简化配置,提高开发效率。
Spring的原始注解主要是代替的配置。

注解 说明
@Component 是用在类上用于实例化Bean
@Controller 使用在WEB层类上用于实例化Bean
@Service 使用在Service层类上用来实例化Bean
@Repository 使用在dao层类上用来实例化Bean
@Autowired 使用在字段上用于根据类型依赖注入
@Qualifier 结合@Autowired一起使用用于根据名称进行依赖注入
@Resource 相当于@Autowired+@Qualifier,按照名称进行注入
@Value 注入普通属性
@Scope 标注Bean的范围
@PostConstruct 使用在方法上标注该方法是Bean的初始化方法
@PreDestory 使用在方法上标注该方法是Bean的摧毁方法

3.2 注解的使用:

user.properties配置文件:

init.name=admin
init.pass=123456

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"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!--   配置自动扫描包-->
    <context:component-scan base-package="com.xxgc.spring"/>
<!--    引入配置文件-->
    <context:property-placeholder location="user.properties"/>
</beans>

java中的启动入口:

package com.xxgc.spring.web;

public class LoginTest {

    @Test
    public void test(){
        //加载配置文件(相当于开启注解扫描)
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        //获取web接口Bean(入口Bean)
        UserLoginWeb userLoginWeb = applicationContext.getBean(UserLoginWeb.class);
        //模拟用户请求
        userLoginWeb.loginUser("张三","123456");
    }
}

java中的基本使用:

package com.xxgc.spring.service.impl;

/**
 * 用户登录业务逻辑实现类
 */

@Service
@Scope("prototype")
public class UserLoginServiceImpl implements IUserLoginService {
    //业务逻辑层和dao建立通信(普通)
    //private UserMapper userMapper = new UserMapperImpl();
    //通过数据类型从Spring 容器找(byType)
//    @Autowired
    //结合@Autowired使用 按照id名称进行匹配(byName)
    //@Qualifier("userMapper")
    //相当于@Autowired + @Qualifier
    @Resource(name="userMapperImpl")
    private UserMapper userMapper;
    //@Value("${init.name}"):引入外部文件里面的数据 如:(init.name=admin)
    @Value("${init.name}")
    private String name;
    //@Value("123")
    @Value("${init.pass}")
    private String pass;
    public int userLogin(String name, String pass) {
        System.out.println("登录的业务逻辑被调用");
        System.out.println("调用dao层,让dao层小弟帮我们去数据库找人。。。");
        System.out.println("name = " + name);
        System.out.println("pass = " + pass);

        userMapper.selectUserByUser(name,pass);

        return 0;
    }

    @PostConstruct
    public void init(){
        System.out.println("类创建时调用");
    }
    @PreDestroy
    public void distory(){
        System.out.println("类销毁时调用");
    }
}

3.3 Spring新注解(核心):

@Configuration 用于指定当前类是一个Spring的配置类,当创建容器时会从该类上加载注解
@ComponentScan 用于指定Spring在初始化容器要扫描的包,作用和在Spring的xml的配置文件中的一样
@Bean 使用在方法上,标注将该方法的返回值存储到Spring容器中
@PropertySource 用于加载.properties文件中的配置
@Import 用于导入其他配置类

3.3.1 java实现自定义配置类(配置数据源C3P0):

java配置类:

package com.xxgc.spring.config;

/**
 * Spring核心配置类
 *  配置自动扫描包
 *  <context:component-scan base-package="com.xxgc.spring"/>
 *  引入文件
 *  <context:property-placeholder location="user.properties"/>
 */

import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import javax.sql.DataSource;

//@Configuration:指定是核心配置类
@Configuration
//@ComponentScan("com.xxgc.spring"):扫描包
@ComponentScan("com.xxgc.spring")
//@PropertySource({"classpath:user.properties","classpath:jdbc.properties"}):加载文件
@PropertySource({"classpath:user.properties","classpath:jdbc.properties"})
public class SpringConfiguration {
    @Value("${jdbc.test.driver}")
    private String driver;
    @Value("${jdbc.test.url}")
    private String url;
    @Value("${jdbc.test.username}")
    private String username;
    @Value("${jdbc.test.password}")
    private String password;

    /**
        原始的xml配置方式
        配置c3p0数据源
        <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
            <property name="driverClass" value="${jdbc.test.driver}"></property>
            <property name="jdbcUrl" value="${jdbc.test.url}"></property>
            <property name="user" value="${jdbc.test.username}"></property>
           <property name="password" value="${jdbc.test.password}"></property>
        </bean>
     */

    //java版本的配置方法(返回一个数据源)
    //@Bean("dataSource"):将DataSource注入Spring容器 获取是使用自定义的名字(dataSource)
    @Bean("dataSource")
    public DataSource getDataSource() throws Exception {
        //实例化数据源并配置
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setDriverClass(driver);
        dataSource.setJdbcUrl(url);
        dataSource.setUser(username);
        dataSource.setPassword(password);
        return dataSource;

    }
}

测试:

    @Test
    public void test() throws SQLException {
        //加载配置文件、
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfiguration.class);

        //拿到数据源
        DataSource dataSource = (DataSource) applicationContext.getBean("dataSource");
        //获取连接
        Connection connection = dataSource.getConnection();
        System.out.println("connection = " + connection);
        //归还链接
        connection.close();


    }

4》Spring整合Junit:

4.1导入依赖:

 <!-- spring整合Junit-->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-test</artifactId>
  <version>5.1.9.RELEASE</version>
</dependency>

4.2 编写java测试:

package com.xxgc.spring.web;

//@RunWith(SpringJUnit4ClassRunner.class):代表Junit增强
@RunWith(SpringJUnit4ClassRunner.class)
//加载配置类
@ContextConfiguration(classes = SpringConfiguration.class)
public class SpringJunitTest {
    @Autowired
    private DataSource dataSource;
    @Test
    public void test() throws SQLException {
        Connection connection = dataSource.getConnection();
        System.out.println("connection = " + connection);
        connection.close();
    }
}

5》Spring整合WEB环境:

应用上下文通过new AnnotationConfigApplicationContext(SpringConfiguration.class);方法来创建Spring容器,但是在web项目中,不可能每次请求都去创建一次Spring容器,应该在项目第一次启动就加载好Spring容器,把容器存储到最大的作用域中ServletContext,这样就可以任何位置从作用域中获取应用上下文Application对象。

1.4.Bean的作用域

Spring笔记: - 图5

 <bean id="ServiceImpl" class="cn.csdn.service.ServiceImpl" scope="singleton"> /*scope:设置对应作用域*/

1.5.Bean的自动装配

Spring的自动装配需要从两个角度来实现,或者说是两个操作:

  1. 组件扫描(component scanning):spring会自动发现应用上下文中所创建的bean;
  2. 自动装配(autowiring):spring自动满足bean之间的依赖,也就是我们说的IoC/DI;

    1.5.1.autowire byName (按名称自动装配 依据set方法注入)

    ```xml //autowire=”byname” 按照名字进行装配
**小结:**<br />当一个bean节点带有 autowire byName的属性时。

1. 将查找其类中所有的set方法名,例如setCat,获得将set去掉并且首字母小写的字符串,即cat。
1. 去spring容器中寻找是否有此字符串名称id的对象。
1. 如果有,就取出注入;如果没有,就报空指针异常。
<a name="6iRkA"></a>
### 1.5.2.autowire byType (按类型自动装配)
**ps:**使用autowire byType首先需要保证:同一类型的对象,在spring容器中唯一。如果不唯一,会报不唯一的异常。

NoUniqueBeanDefinitionException

<a name="pgMAT"></a>
### 1.5.3.使用注解进行自动装配
<a name="khUXk"></a>
#### 1、在spring配置文件中引入context文件头
```xml
xmlns:context="http://www.springframework.org/schema/context"

http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd

2、开启属性注解支持!

<context:annotation-config/>

3.@Autowired

  • @Autowired是按类型自动转配的,不支持id匹配。
  • 需要导入 spring-aop的包!

    4.@Qualifier(与@Autowired搭配使用)

  • @Autowired是根据类型自动装配的,加上@Qualifier则可以根据byName的方式自动装配

  • @Qualifier不能单独使用。

    @Autowired与@Resource异同:

    1、@Autowired与@Resource都可以用来装配bean。都可以写在字段上,或写在setter方法上。
    2、@Autowired默认按类型装配(属于spring规范),默认情况下必须要求依赖对象必须存在,如果要允许null 值,可以设置它的required属性为false,如:@Autowired(required=false) ,如果我们想使用名称装配可以结合@Qualifier注解进行使用
    3、@Resource(属于J2EE复返),默认按照名称进行装配,名称可以通过name属性进行指定。如果没有指定name属性,当注解写在字段上时,默认取字段名进行按照名称查找,如果注解写在setter方法上默认取属性名进行装配。当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。
    它们的作用相同都是用注解方式注入对象,但执行顺序不同。@Autowired先byType,@Resource先byName。

    Spring自定义配置:

    ```java @Configuration //代表这是一个配置类 @Import(MyConfig2.class) //导入合并其他配置类,类似于配置文件中的 inculde 标签 public class MyConfig {

    @Bean //通过方法注册一个bean,这里的返回值就Bean的类型,方法名就是bean的id! public Dog dog(){

     return new Dog();
    

    }

}

<a name="tikkV"></a>
# 四、SpringAOP:
<a name="557d4aa2"></a>
### 1.1.什么是AOP:
AOP(Aspect Oriented Programming)意为:面向切面编程(通过预编译方式和运行期动态代理实现),通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。<br />![](https://cdn.nlark.com/yuque/0/2021/png/13022516/1633437939612-53f9b5b9-66e2-4bbc-8859-de1437f9befe.png#height=551&id=loCVR&originHeight=551&originWidth=952&originalType=binary&ratio=1&size=0&status=done&style=none&width=952)
<a name="mJqN9"></a>
### 1.2.Aop在Spring中的作用:
作用:在程序运行期间,在不修改代码的情况下对方法进行增强。<br />优势:减少重复代码,提高开发效率,并且便于维护。

- 切面(ASPECT):横切关注点 被模块化 的特殊对象(切点和通知的结合)。即,它是一个类。
- 通知(Advice):切面必须要完成的工作(要做的事情)。即,它是类中的一个方法。
- 目标(Target):被通知对象(要代理的对象)。
- 代理(Proxy):向目标对象应用通知之后创建的对象。   
- 切入点(PointCut):切面通知 执行的 “地点”的定义(对那些连接点的哪些方法进行增强的定义)。
- 连接点(JointPoint):与切入点匹配的执行点(在spring中这些点指的是方法)。
- 织入(Weaving):是指吧增强应用到目标对象来创建新的代理对象的过程。(切入点+通知的过程)
- ![image.png](https://cdn.nlark.com/yuque/0/2021/png/13022516/1637807630639-abab5e9b-6196-46f7-9331-4775098779e2.png#clientId=u3eb206a7-83b2-4&from=paste&height=641&id=ueb668dad&margin=%5Bobject%20Object%5D&name=image.png&originHeight=641&originWidth=1479&originalType=binary&ratio=1&size=333197&status=done&style=none&taskId=uc4950b7b-c074-4fb7-928c-f56ce4975e8&width=1479)

- ![](https://cdn.nlark.com/yuque/0/2021/png/13022516/1633438006689-06dd84a6-c58c-4acb-b13b-ee135083fc75.png#height=471&id=iVOvW&originHeight=471&originWidth=744&originalType=binary&ratio=1&size=0&status=done&style=none&width=744)
<a name="KVw1m"></a>
### ![](https://cdn.nlark.com/yuque/0/2021/png/13022516/1633438043424-d7e025a9-64d0-42fe-8411-32b4c094c987.png#height=474&id=UMhr3&originHeight=474&originWidth=762&originalType=binary&ratio=1&size=0&status=done&style=none&width=762)
<a name="ChB0r"></a>
### 1.2.2 Aop底层使用哪一种代理方式?
在Spring中,框架会根据目标类是否实现接口来确定采用哪种动态代理模式。
<a name="VRXqB"></a>
## 1.3 AOP的动态代理技术:
常用的动态代理技术:
<a name="mJKus"></a>
### 1.JDK代理:
基于接口的动态代理技术
<a name="Wf5IG"></a>
#### 1.1 创建增强类UserEnhance:
```java
public class UserEnhance {
    public void before(){
        System.out.println("前置增强方法");
    }
    public void after(){
        System.out.println("后置增强方法");
    }
}

1.2 创建登录接口+登录实现类:

public interface IUserLogin {
    /**
     * 用户的登录方法
     * @param name
     * @param pass
     * @return
     */
    boolean userLogin(String name,String pass);
}
public class UserLogin implements IUserLogin{
    @Override
    public boolean userLogin(String name, String pass) {
        System.out.println("正在执行方法.....");
        if("333".equals(pass) && "zhangsan".equals(name)){

            return true;
        }
        return false;
    }
}

1.3 创建测试类:

public class UserLoginTest {
    @Test
    public void test(){
        //目标增强方法
        UserLogin userLogin = new UserLogin();
        //增强类
        UserEnhance userEnhance = new UserEnhance();
        IUserLogin iUserLogin = (IUserLogin) Proxy.newProxyInstance(
                userLogin.getClass().getClassLoader(), //目标对象类加载器
                userLogin.getClass().getInterfaces(),//目标对象相同的字节码对想组
                new InvocationHandler() {//代理
                    //调用其任何方法最终执行的都是invoke方法
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                       userEnhance.before();
                         //invoke接的返回值是执行方法的返回值
                        Object invoke = method.invoke(userLogin, args);
                       userEnhance.after();
                        return invoke;
                    }
                }
        );
        boolean b = iUserLogin.userLogin("zhangsan", "332");
        System.out.println("结果 = " + b);
    }

1.4 执行结果:

image.png

2.cglib代理:

基于父类的动态代理技术

2.1 创建增强类MoneyEnhance.java:

public class MoneyEnhance {
    public void before(Object[] args){
        args[2]=5;
        System.out.println("前置增强方法");
    }
    public void after(){

        System.out.println("后置增强方法");
    }
}

2.2 创建功能类PayMoney.java:

public class PayMoney {
    public boolean userPay(int uId,int money,int toUId){
        System.out.println("用户:"+uId+"给"+toUId+"转账"+money+"元");
        return true;
    }
}

2.3 创建测试类:

public class MoneyTest {
    //黑名单
    private static int[] users = new int[]{1,3,5};
    @Test
    public void test(){

        //实现目标对象
        PayMoney payMoney = new PayMoney();
        //实现增强对象
        MoneyEnhance moneyEnhance = new MoneyEnhance();

        //1.创建增强器
        Enhancer enhancer = new Enhancer();
        //2.设置父类(要代理谁、给谁增强)
        enhancer.setSuperclass(PayMoney.class);
        //3.设置回调(代理对象无论调用哪一个方法都会执行intercept方法)
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                //前置增强
                moneyEnhance.before(args);
                int num = (int)args[0];
                Object invoke = null;
                //判断数组里面是否包含黑名单
                if(Arrays.binarySearch(users,(int)args[0])<0){
                   invoke =  method.invoke(payMoney, args);
                }else {
                    return false;
                }
                //后置增强
                moneyEnhance.after();
                return invoke;
            }
        });
        //4.创建代理
        PayMoney pm = (PayMoney) enhancer.create();
        pm.userPay(2,100,8);


    }
}

3.使用Spring实现Aop;

Spring的AOP实现的方式(底层)就是对上面的动态代理进行封装,封装后我们只需要对需要关心的部分进行代码编写,并通过配置的方式完成指定的目标方法的增强。
1.使用AOP织入,需要导入一个依赖包!

<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
   <groupId>org.aspectj</groupId>
   <artifactId>aspectjweaver</artifactId>
   <version>1.9.4</version>
</dependency>

第一种方式:Spring API 实现:

1.首先编写我们的业务接口和实现类:

public interface UserService {

   public void add();

   public void delete();

   public void update();

   public void search();

}
public class UserServiceImpl implements UserService{

   @Override
   public void add() {
       System.out.println("增加用户");
  }

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

   @Override
   public void update() {
       System.out.println("更新用户");
  }

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

2.然后去写我们的增强类 , 我们编写两个 , 一个前置增强 一个后置增强

public class Log implements MethodBeforeAdvice {

   //method : 要执行的目标对象的方法
   //objects : 被调用的方法的参数
   //Object : 目标对象
   @Override
   public void before(Method method, Object[] objects, Object o) throws Throwable {
       System.out.println( o.getClass().getName() + "的" + method.getName() + "方法被执行了");
  }
}
public class AfterLog implements AfterReturningAdvice {
   //returnValue 返回值
   //method被调用的方法
   //args 被调用的方法的对象的参数
   //target 被调用的目标对象
   @Override
   public void afterReturning(Object returnValue, Method method, Object[] args,Object target) throws Throwable {
       System.out.println("执行了" + target.getClass().getName()
       +"的"+method.getName()+"方法,"
       +"返回值:"+returnValue);
  }
}

3.最后去spring的文件中注册 , 并实现aop切入实现 , 注意导入约束

<?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:aop="http://www.springframework.org/schema/aop"
      xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">

   <!--注册bean-->
   <bean id="userService" class="com.kuang.service.UserServiceImpl"/>
   <bean id="log" class="com.kuang.log.Log"/>
   <bean id="afterLog" class="com.kuang.log.AfterLog"/>

   <!--aop的配置-->
   <aop:config>
       <!--切入点 expression:表达式匹配要执行的方法-->
       <aop:pointcut id="pointcut" expression="execution(* com.kuang.service.UserServiceImpl.*(..))"/>
       <!--执行环绕; advice-ref执行方法 . pointcut-ref切入点-->
       <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
       <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
   </aop:config>

</beans>

4.测试

public class MyTest {
   @Test
   public void test(){
       ApplicationContext context = newClassPathXmlApplicationContext("beans.xml");
       UserService userService = (UserService) context.getBean("userService");
       userService.search();
  }
}

第二种方式:自定义类来实现Aop:

目标业务类不变依旧是userServiceImpl
第一步 : 写我们自己的一个切入类

public class DiyPointcut {

    public void before(){

        System.out.println("前置增强方法");
    }
    public void after(){

        System.out.println("最终增强方法");
    }


    public String around(ProceedingJoinPoint proceedingJoinPoint) {
        System.out.println("环绕前。。。");
        String proceed = "";
        try {
            //获取参数
            Object[] args = proceedingJoinPoint.getArgs();
            //放行
            proceed = (String)proceedingJoinPoint.proceed(args);
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        System.out.println("环绕后。。。");
        return proceed;
    }
    public void afterReturning() {
        System.out.println("后置增强方法");
    }

    public void afterThrowing(){
        System.out.println("异常了。。。");
    }

}

第二步:配置xml

<!--第二种方式自定义实现-->
<!--注册bean-->
<bean id="diy" class="com.kuang.config.DiyPointcut"/>

<!--aop的配置-->
<aop:config>
   <!--第二种方式:使用AOP的标签实现-->
    <aop:pointcut id="iuserlogin" expression="execution(* com.xxgc.aop.jdk.springAop.UserLoginSpringAop.getAdminMsg(..))"/>
        <!--声明切面-->
      <aop:aspect ref="diy">
          <!--通知类型-->
          <!--前置-->
          <aop:before method="before" pointcut-ref="iuserlogin"/>
          <!--最终-->
          <aop:after-returning method="afterReturning" pointcut-ref="iuserlogin"/>
          <!--后置-->
          <aop:after method="after" pointcut-ref="iuserlogin"/>
          <!--环绕-->
          <aop:around method="around" pointcut-ref="iuserlogin"/>
             <!--异常通知-->
          <aop:after-throwing method="afterThrowing" pointcut-ref="iuserlogin"/>
      </aop:aspect>
</aop:config>

第三种方式:使用注解方式

第一步:创建目标接口和目标类(内部有连接点)

@Service
public class UserLoginServiceImpl implements IUserLogin {
    /**
     * @Author 小唐
     * @param user 修改信息
     * @return
     */
    public Integer updateUserById(User user) {
        System.out.println("修改用户");
        return null;
    }

    public Integer insertUser(User user) {
        System.out.println("插入用户");
        return null;
    }

    public List<User> selectUserByName(String name) {
        System.out.println("根据用户名查询用户信息");
        return null;
    }

    public User selectUserById(Integer uId) {
        System.out.println("根据用户ID查询用户信息");
        return null;
    }
}

第二步:编写一个注解实现的增强类(内部有通知、增强方法)

package com.kuang.config;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
//把这个类交个spring  并且告诉spring这是一个切面类
@Component
@Aspect
public class AnnotationPointcut {
   @Before("execution(* com.kuang.service.UserServiceImpl.*(..))")
   public void before(){
       System.out.println("---------方法执行前---------");
  }

   @After("execution(* com.kuang.service.UserServiceImpl.*(..))")
   public void after(){
       System.out.println("---------最终方法执行后---------");
  }
    @AfterReturning(value = "execution(* com.xxgc.spring.service.impl.UserLoginServiceImpl.select*(..))")
    public void afterReturning(){
        System.out.println("后置增强。。。");
    }
    @AfterThrowing(value = "execution(* com.xxgc.spring.service.impl.UserLoginServiceImpl.*(..))")
    public void afterThrowing(){
        System.out.println("异常通知。。。");
    }
   @Around("execution(* com.kuang.service.UserServiceImpl.*(..))")
   public void around(ProceedingJoinPoint jp) throws Throwable {
       System.out.println("环绕前");
       System.out.println("签名:"+jp.getSignature());
       //执行目标方法proceed
       Object proceed = jp.proceed();
       System.out.println("环绕后");
       System.out.println(proceed);
  }
}

第三步:在Spring配置文件中,注册bean,并增加支持注解的配置

<!--第三种方式:注解实现-->
<bean id="annotationPointcut" class="com.kuang.config.AnnotationPointcut"/>
<!--开启注解扫描-->
<context:component-scan base-package="com.xxgc.spring"/>
<aop:aspectj-autoproxy/>
通过aop命名空间的<aop:aspectj-autoproxy />声明自动为spring容器中那些配置@aspectJ切面的bean创建代理,织入切面。
当然,spring 在内部依旧采用AnnotationAwareAspectJAutoProxyCreator进行自动代理的创建工作,
但具体实现的细节已经被<aop:aspectj-autoproxy />隐藏起来了

<aop:aspectj-autoproxy />有一个proxy-target-class属性,默认为false,表示使用jdk动态代理织入增强,
当配为<aop:aspectj-autoproxy  poxy-target-class="true"/>时,
表示使用CGLib动态代理技术织入增强。不过即使proxy-target-class设置为false,
如果目标类没有声明接口,则spring将自动使用CGLib动态代理。

第四步:测试

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class UserLoginService {
    @Autowired
    private IUserLogin iUserLogin;

    @Test
    public void test(){
        iUserLogin.insertUser(new User());
    };
}

ps:/*
环绕前置报错目标方法不执行
前置增强方法中报错目标方法和异常增强都不会执行
/

Aop注解通知类型:

@Before 前置通知 指定增强方法在切点方法之前执行
@AfterReturning 后置通知 指定增强方法在切点方法之后执行
@After 最终通知 无论增强方法执行是否异常都会执行
@AfterThrowing 异常通知 方法抛出异常执行
@Around 环绕通知 目标方法前后都执行

注解Aop Excution切点表达式的抽离:

    /**
     * 抽出切点表达式
     */
    @Pointcut("execution(* com.xxgc.spring.service.impl.UserLoginServiceImpl.select*(..))")
    public void myPoint(){}

    /**
    *引入上面抽离切点表达式的方法
    */
    @Before("UserLoginAspect.myPoint()")
    public void before(){
        System.out.println("前置增强。。。");
    }

4、execution()切点表达式的6种写法:

4.1 execution(public boolean com.xxgc.aop.spring.UserLoginImpl.login(String,String));

//完整路径+有参数
execution(权限修饰符 返回值类型 包名.类名.方法名(参数类型,参数类型));

4.2 execution(public boolean com.xxgc.aop.spring.UserLoginImpl.login());

//完整路径+无参数

4.2 execution(public boolean com.xxgc.aop.spring.UserLoginImpl.get*(..));

*:代表任意 ..:代表多个任意的类型

//匹配类下面的以get开头的方法+任意参数

4.3 execution(boolean com.xxgc.aop.spring..(..));

//spring包下面的所有类+所有方法+任意参数

4.4 execution(* com.xxgc.aop.spring.UserLoginImpl.login(String,String));

//任意返回值类型+完整切点信息

4.5 execution( boolean com.xxgc.aop.spring...*(..));

spring..*:代表spring路径下的所有包下面的的任意方法

//任意访问修饰符+spring包下多层路径下的任意类+任意方法+任意参数

5、Aop的通知类型:

(1)前置通知(Before advice):在某连接点(join point)之前执行的通知,但这个通知不能阻止连接点前的执行(除非它抛出一个异常)。

(2)返回后通知(After returning advice):在某连接点(join point)正常完成后执行的通知:例如,一个方法没有抛出任何异常,正常返回。

(3)抛出异常后通知(After throwing advice):在方法抛出异常退出时执行的通知。

(4)后通知(After (finally) advice):当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)。

(5)环绕通知(Around Advice):包围一个连接点(join point)的通知,如方法调用。这是最强大的一种通知类型。 环绕通知可以在方法调用前后完成自定义的行为。它也会选择是否继续执行连接点或直接返回它们自己的返回值或抛出异常来结束执行。 环绕通知是最常用的一种通知类型。大部分基于拦截的AOP框架,例如Nanning和JBoss4,都只提供环绕通知。
原文链接:https://blog.csdn.net/lovedingd/article/details/108026587

通知执行顺序:

同一个aspect,不同advice的执行顺序:

①没有异常情况下的执行顺序:

around before advice
before advice
target method 执行
around after advice
after advice
afterReturning

②有异常情况下的执行顺序:

around before advice
before advice
target method 执行
around after advice
after advice
afterThrowing:异常发生
java.lang.RuntimeException: 异常发生

通知类型的配置语法:

5.1 =>前置通知

<aop:before  method="切面方法" pointcut="excution表达式"/>

5.2 =>后置通知

<aop:after  method="切面方法" pointcut="excution表达式"/>

5.3 =>最终通知

<aop:after-returning  method="切面方法" pointcut="excution表达式"/>

5.4 =>环绕通知

环绕通知的增强方法:

   public String around(ProceedingJoinPoint proceedingJoinPoint) {
        System.out.println("环绕前。。。");
        String proceed = "";
        try {
            //获取参数
            Object[] args = proceedingJoinPoint.getArgs();
            //放行
            proceed = (String)proceedingJoinPoint.proceed(args);
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        System.out.println("环绕后。。。");
        return proceed;
    }
<aop:around method="环绕方法" pointcut-ref="连接点Beanid"/>

5.5 =>异常通知

<aop:after-throwing method="异常增强方法" pointcut-ref="连接点Beanid"/>

五、Spring 事务:

5.1 什么是事务?

事务必须服从ACID原则。ACID指的是原子性(atomicity)、一致性(consistency)、隔离性(isolation)
和持久性(durability)。通俗理解,事务其实就是一系列指令的集合。

  • 原子性:操作这些指令时,要么全部执行成功,要么全部不执行。只要其中一个指令执行失败, 所有的指令都执行失败,数据进行回滚,回到执行指令前的数据状态。
  • 一致性:事务的执行使数据从一个状态转换为另一个状态,但是对于整个数据的完整性保持稳定。
  • 隔离性:在该事务执行的过程中,无论发生的任何数据的改变都应该只存在于该事务之中, 对外界不存在任何影响。只有在事务确定正确提交之后,才会显示该事务对数据的改变。 其他事务才能获取到这些改变后的数据。
  • 持久性:当事务正确完成后,它对于数据的改变是永久性的。

    5.2 并发事务导致的问题:

    ``` 在许多事务处理同一个数据时,如果没有采取有效的隔离机制,那么并发处理数据时,会带来一些的问题。

(1)第一类丢失更新:撤销一个事务时,把其他事务已提交的更新数据覆盖。

小明去银行柜台存钱,他的账户里原来的余额为100元,现在打算存入100元。在他存钱的过程中,

银行年费扣了5元,余额只剩95元。突然他又想着这100元要用来请女朋友看电影吃饭,不打算存了。

在他撤回存钱操作后,余额依然为他存钱之前的100元。所以那5块钱到底扣了谁的?

(2)脏读:一个事务读取到另一个事务未提交的更新数据。

小明的银行卡余额里有100元。现在他打算用手机点一个外卖饮料,需要付款10元。但是这个时候,

他的女朋友看中了一件衣服95元,她正在使用小明的银行卡付款。于是小明在付款的时候,

程序后台读取到他的余额只有5块钱了,根本不够10元,所以系统拒绝了他的交易,

告诉余额不足。但是小明的女朋友最后因为密码错误,无法进行交易。小明非常郁闷,

明明银行卡里还有100元,怎么会余额不足呢?(他女朋友更郁闷。。。)

(3)幻读也叫虚读:一个事务执行两次查询,第二次结果集包含第一次中没有或某些行已经

被删除的数据,造成两次结果不一致,只是另一个事务在这两次查询中间插入或删除了数据造成的。

公司财务A在进行员工薪资核算业务,需要对小明的工资进行计算并录入系统,必须查询两次明细信息,

然后将后一次的明细信息计算总数出来。财务在第一次明细查询时,基本工资2000元,全勤奖1000元,

提成2000元,共计5000元。在进行第二次计算时,财务B突然接到通知,需要把下个月的节日福利也

在这个月的工资中发放,每人100元。于是财务B在每个人的工资明细中又加了一条节日福利100元。

而此时财务A获得第二次查询小明的工资明细后,发现工资明细变成了4条数据,总数是5100元。

两次计算结果相差100元,财务A奇怪这多出来的一条明细100元是哪里来的呢?(都怪财务B不告诉A。。。)

(4)不可重复读:一个事务两次读取同一行的数据,结果得到不同状态的结果,中间正好另一个事务更新

了该数据,两次结果相异,不可被信任。

小明在手机上购买起购价为1W元理财产品。系统首先要判断他的余额够不够购买理财产品,如果足够再获

取当前的余额,进行申请。系统第一次读取到小明的余额还剩1W元,刚好足够购买产品。但是这个时候刚好

他女朋友又看中了一个包包5000元,这次密码终于不会再错误的女朋友毫不犹豫刷了小明的银行卡买下了这

个包包。但是这个系统刚好在进行第二次确认,发现小明的余额上只有5000元,

根本不够购买。于是系统很生气,拒绝了小明的购买行为,告诉他,你真是个骗子!!!

(5)第二类丢失更新:是不可重复读的特殊情况。如果两个事物都读取同一行,然后两个都进行写操作,

并提交,第一个事物所做的改变就会丢失。

小明和女朋友一起去逛街。女朋友看中了一支口红,(对,女朋友就是用来表现买买买的)小明大方的掏出

了自己的银行卡,告诉女朋友:亲爱的,随便刷,随便买,我坐着等你。然后小明就坐在商城座椅上玩手机,

等着女朋友。这个时候,程序员的聊天群里有人推荐了一本书,小明一看,哎呀,真是本好书,还是限量发行呢,

我一定更要买到。于是小明赶紧找到购买渠道,进行付款操作。而同时,小明的女朋友也在不亦乐乎的买买买,

他们同时进行了一笔交易操作,但是这个时候银行系统出了问题,当他们都付款成功后,

却发现,银行只扣了小明的买书钱,却没有扣去女朋友此时交易的钱。哈哈哈,小明真是太开心了! ```

5.3 了解Spring事务的三个对象:

5.3.1 PlatformTransactionManager:

PlatformTransactionManager接口是Spring的事务管理器,它里面 提供了我们常用的事务方法。

方法 说明
TransactionStatus getTransaction(TransactionDefination defination) 获取事务的状态信息
void commit(TransactionStatus status) 提交事务
void rollback(TransactionStatus status) 回滚事务

注意:
PlatfromTransactionManager是接口类型,不同的dao层技术则有不同的实现类,例如:
Dao层技术是jdbc或mybatis时:
org.springframework.jdbc.datasource.DataSourceTransactionManager
Dao层技术是hibernate时:
org.springframework.orm.hibernate5.HibernateATransactionManager

5.3.2 TransactionDefinition:

TransactionDefinition是事物的定义信息对象,里面有如下方法:

方法 说明
int getlsolationLevel() 获取事物的隔离级别
int getPropogationBehavior() 获取事物的传播行为
int getTimeout() 获取超时时间
boolean isReadOnly() 是否只读

事务隔离级别:

设置事务隔离级别,可以解决事务并发产生的问题,如脏读、不可重复读和虚读/幻读。

事务的隔离级别:

级别 说明
ISOLATION_DEFAULT 使用数据库默认的事务隔离级别
ISOLATION_READ_UNCOMMITTED 允许读取尚未提交的修改,可能导致脏读、幻读和不可重复读
ISOLATION_READ_COMMITTED 允许从已经提交的事务读取,可防止脏读、但幻读,不可重复读仍然有可能发生
ISOLATION_PEPEATABLE_READ 对相同字段的多次读取的结果是一致的,除非数据被当前事务自生修改。可防止脏读和不可重复读,但幻读仍有可能发生
ISOLATION_SERIALIZABLE 完全服从acid隔离原则,确保不发生脏读、不可重复读、和幻读,但执行效率最低。


事物的传播行为:

事务传播行为类型 说明
PROPAGATION_REQUIRED 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。
PROPAGATION_SUPPORTS 支持当前事务,如果当前没有事务,就以非事务方式执行(没有事务)。
PROPAGATION_MANDATORY 使用当前的事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW 新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER 以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。

超时时间:默认值是-1,没有超时限制。如果有,一秒为单位进行设置。
是否只读:建议查询时设置为只读。

5.3.3 TransactionStatus:

TransactionStatus接口提供的是事务具体的运行状态,方法介绍如下:

方法 说明
boolean hasSavepoint() 是否存储回滚点
boolean isCompleted() 事务是否完成
boolean isNewTransaction() 是否是新事务
boolean isRollbackOnly() 事务是否回滚

5.4 声明式事务控制:

Spring的声明式事务顾名思义就是采用声明的方式来处理事务。这里所说的声明,就是指在配置文件中声明,用在Spring配置文件中声明式的处理事务来代替代码式的处理事务。

5.4.1 声明式事务处理的作用:

1》事务管理不入侵开发的组件。具体来说,业务逻辑对象就不会意识到正在事务管理之中,事实上也应该如此,因为事务管理是属于系统层面的服务,而不是业务逻辑的一部分,如果想要改变事务管理策略的话,也只需要在定义文件中重新配置即可。
2》在不需要事务管理的时候,只有在设定文件上修改一下,即可移去事务管理服务,无需改变代码重新编译,这样维护起来极其方便。
注意:Spring声明式事务控制就是用的AOP

5.5 Spring事务的常用属性:

@Transactional:

事务注解一般在方法中写上这个注解就可以了,当然有一些比较复杂的业务场景,需要自己了解一些事务的属性;这样对自己的写的业务也比较放心。代码也会更加的可靠和安全;


readOnly

该属性用于设置当前事务是否为只读事务,设置为true表示只读,false则表示可读写,默认值为false。例如:@Transactional(readOnly=true)

rollbackFor

该属性用于设置需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,则进行事务回滚。例如:
指定单一异常类:@Transactional(rollbackFor=RuntimeException.class)
指定多个异常类:@Transactional(rollbackFor={RuntimeException.class, Exception.class})

rollbackForClassName

该属性用于设置需要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,则进行事务回滚。例如:
指定单一异常类名称:@Transactional(rollbackForClassName=“RuntimeException”)
指定多个异常类名称:@Transactional(rollbackForClassName={“RuntimeException”,“Exception”})

noRollbackFor

该属性用于设置不需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,不进行事务回滚。例如:
指定单一异常类:@Transactional(noRollbackFor=RuntimeException.class)
指定多个异常类:@Transactional(noRollbackFor={RuntimeException.class, Exception.class})

noRollbackForClassName

该属性用于设置不需要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,不进行事务回滚。例如:
指定单一异常类名称:@Transactional(noRollbackForClassName=“RuntimeException”)
指定多个异常类名称:@Transactional(noRollbackForClassName={“RuntimeException”,“Exception”})

propagation

该属性用于设置事务的传播行为。
例如:@Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true)
@Transactional(propagation=Propagation.REQUIRED) :如果有事务, 那么加入事务, 没有的话新建一个(默认情况下)
@Transactional(propagation=Propagation.NOT_SUPPORTED) :容器不为这个方法开启事务
@Transactional(propagation=Propagation.REQUIRES_NEW) :不管是否存在事务,都创建一个新的事务,原来的挂起,新的执行完毕,继续执行老的事务
@Transactional(propagation=Propagation.MANDATORY) :必须在一个已有的事务中执行,否则抛出异常
@Transactional(propagation=Propagation.NEVER) :必须在一个没有的事务中执行,否则抛出异常(与Propagation.MANDATORY相反)
@Transactional(propagation=Propagation.SUPPORTS) :如果其他bean调用这个方法,在其他bean中声明事务,那就用事务.如果其他bean没有声明事务,那就不用事务.
isolation
该属性用于设置底层数据库的事务隔离级别,事务隔离级别用于处理多事务并发的情况,通常使用数据库的默认隔离级别即可,基本不需要进行设置

@Transactional(isolation = Isolation.READ_UNCOMMITTED):读取未提交数据(会出现脏读, 不可重复读) 基本不使用;
就是俗称“脏读”(dirty read),在没有提交数据时能够读到已经更新的数据
@Transactional(isolation = Isolation.READ_COMMITTED):读取已提交数据(会出现不可重复读和幻读);在一个事务中进行查询时,允许读取提交前的数据,数据提交后,当前查询就可以读取到数据。update数据时候并不锁住表
@Transactional(isolation = Isolation.REPEATABLE_READ):可重复读(会出现幻读);在一个事务中进行查询时,不允许读取其他事务update的数据,允许读取到其他事务提交的新增数据
@Transactional(isolation = Isolation.SERIALIZABLE):串行化;在一个事务中进行查询时,不允许任何对这个查询表的数据修改。
timeout
该属性用于设置事务的超时秒数,默认值为-1表示永不超时

@Transactional(timeout=30) //默认是30秒

六、spring容器:

1.1 spring两个核心接口:

1、BeanFactory:

BeanFactory负责配置、创建、管理bean 它的子接口是ApplicationContext
BeanFactory下的方法:
1.boolean containceBean(String name):判断容器是否包好id为name的Bean;
2.Object getBean(String name):返回容器id为name的Bean实例;
3.Class<?> getType(String name):返回容器id为name的Bean实例的类型;
ps:BeanFactory常用的实现类是 DefaultListbleBeanFactory

2.ApplicationContext:

ApplicationContext 是BEanFactory的子接口 ,
常用实现类有
FileSystemXmlAoolicationContext,
ClassPathXmlApplicationContext,
AnnotationConfigApplicationContext
功能
1.Web应用启动时自动创建ApplicationContext
2.默认会预初始化所有的singleton Bean ,也可通过配置取消
3.默认继承MessageSource接口,提供国际化功能
4.资源访问
5.事件机制
6.同时加载多个配置文件
7.已声明式方式启动并创建Spring容器
例如以下配置:
QQ图片20211009150404.jpg