史上最全讲解:Spring详解



Spring第一天

高级课程安排

阶段一

​ SSM框架(Spring SpringMVC MyBatis)

​ FreeMaker模板引擎

​ EasyUI框架

​ CRM项目(后台管理项目)

阶段二

​ 商城项目

​ Dobbu分布式

​ Redis缓存

​ RabbitMQ消息队列

​ Spring Boot框架

​ Spring Cloud微服务

Spring框架

​ Spring框架是一个开源的JavaEE应用程序

​ 主要核心是Ioc(控制反转/依赖注入) 与Aop(面向切面)两大技术

​ Spring IOC

​ Spring AOP

​ Spring JDBC+事务

Spring作用

Dao层

​ JDBC处理(BaseDao)

​ 对应的框架:MyBatis

Service层

​ Spring框架不是针对Service层业务逻辑 没有适合的框架

Web层(Controller层)

​ Servlet(接收参数 响应数据 地址配置 页面转发)

​ 对应的框架:SpringMVC

Spring基于分布式的应用程序

​ 基于轻量级的框架

​ 配置管理

​ Bean对象的实例化-IOC

​ 集成其他的第三方技术

​ MyBatis、Hibernate框架(持久层框架)

​ SpringMVC

​ Spring Security权限​

​ Quartz时钟框架(定时任务处理)

​ 自带的服务

​ Mail 邮件处理

​ 定时任务处理-定时调度(定时短信、定时任务)

​ 消息处理(异步处理)

Spring模块划分

​ Spring IOC模块:Bean对象的实例化 Bean的创建

​ Spring AOP模块:动态代理 面向切面编程

​ Spring JDBC+事务模块

​ Spring Web模块

Spring的环境搭建

1、新建Maven项目

选择"File" —> 选择"New" —> 选择Maven

2、设置项目的坐标
3、设置项目的Maven环境
4、设置项目的名称和存放的工作空间
5、修改项目中pom.xml的配置

5.1 修改JDK版本

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
  </properties>

5.2 修改单元测试的版本

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

5.3 删除build标签中的pluginManagement标签

 <!--删除build标签中的pluginManagement标签-->
  <build>
  </build>

5.4 添加Spring的依赖坐标

Maven仓库:https://mvnrepository.com/

<!--添加Spring的依赖坐标-->
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>4.3.9.RELEASE</version>
</dependency>
6、添加Spring的配置文件 spring.xml

6.1 在项目的src下创建文件夹(Alt+insert)

6.2 将文件夹标注为资源目录

6.3 新建xml文件,将官方文档上的配置拷贝到文件中

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

    <bean id="..." class="...">
        <!-- collaborators and configuration for this bean go here -->
    </bean>

    <bean id="..." class="...">
        <!-- collaborators and configuration for this bean go here -->
    </bean>

    <!-- more bean definitions go here -->

</beans>

7、创建Bean对象
8、通过Spring.xml配置Bean对象
<bean id="userService" class="com.shsxt.service.UserService"></bean>
9、加载配置文件,获取实例化对象
 public static void main(String[] args) {
        // 获取Spring上下文环境
        ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml");
        // 通过getBean方法得到Spring容器中实例化好的Bean对象
        UserService userService = (UserService) ac.getBean("userService");
        // 调用方法
        userService.test();
    }

Spring IOC 核心技术

工厂设计模式 (简单工厂 工厂方法 抽象工厂) (大话设计模式.pdf C#)

XML解析 (Dom4J)

反射(实例化对象 反射获取方法 反射获取属性 反射获取构造器 反射方法调用 )

策略模式(加载资源)

单例(IOC创建的实例化对象都是单例)

Spring IOC 配置文件的加载方式

根据相对路径加载资源
ApplicationContext ac  = new ClassPathXmlApplicationContext("spring.xml");
根据绝对路径加载资源(了解)
ApplicationContext ac = new FileSystemXmlApplicationContext("F:\\SHSXT\\Java\\33\\idea_workspace\\spring01\\src\\main\\resources\\spring.xml");
      
同时加载多个资源文件
1. 可变参数,传入多个文件名
ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml","dao.xml");
2. 通过总的配置文件import其他配置文件

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

    <!--导入需要包含的资源文件-->
    <import resource="spring2.xml"/>
    <import resource="dao.xml"/>


</beans>

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

    <!--
        xmlns 即 xml namespace                   xml使用的命名空间
        xmlns:xsi 即xml schema instance          xml 遵守的具体规范
        xsi:schemaLocation                       本文档xml遵守的规范 官方指定
    -->


    <!--
        Bean标签:Bean对象的实例化
            id      唯一标识(bean对象)    值不可重复
            class   类的全路径   包名.类名
    -->
    <bean id="userService" class="com.shsxt.service.UserService"> </bean>



</beans>

dao.xml

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

    <bean id="userDao" class="com.shsxt.dao.UserDao"></bean>
</beans>

加载时只需加载总的配置文件即可

// 加载总的资源文件
ApplicationContext ac = new ClassPathXmlApplicationContext("service.xml");

Spring IOC 对象实例化的三种方式

构造器实例化

注:通过默认构造器创建 空构造方法必须存在 否则创建失败

ApplicationContext ac = new ClassPathXmlApplicationContext("spring2.xml");
UserService userService = (UserService) ac.getBean("userService");  
userService.test();
<bean id="userService" class="com.shsxt.service.UserService"> </bean>
静态工厂实例化(了解)
<!--静态工厂-->
<bean id="userController" class="com.shsxt.factory.StaticFactory" factory-method="createUserService"></bean>

实例化工厂实例化(了解)
<!--实例化工厂-->
<bean id="intanceFactory" class="com.shsxt.factory.IntanceFactory"></bean>
<bean id="userController" factory-bean="intanceFactory" factory-method="crateUser"></bean>

Spring三种实例化bean的方式比较

方式一:通过bean的缺省构造函数创建,当各个bean的业务逻辑相互比较独立的时候或者和外界关联较少的时候可以使用。

方式二:利用静态factory方法创建,可以统一管理各个bean的创建,如各个bean在创建之前需要相同的初始化处理,则可用这个factory方法险进行统一的处理等等。

方式三:利用实例化factory方法创建,即将factory方法也作为了业务bean来控制,

1可用于集成其他框架的bean创建管理方法,2能够使bean和factory的角色互换。

Spring第二天

Spring IOC注入(DI)

Spring IOC 手动装配(注入)

set(方法)注入 推荐使用
1、属性字段提供set方法
public class UserService {

    // 业务对象UserDao set注入(提供set方法)
    private UserDao userDao;
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    // 业务对象StudentDao  set注入(提供set方法)
    private StudentDao studentDao;
    public void setStudentDao(StudentDao studentDao) {
        this.studentDao = studentDao;
    }

    // 常用对象String  set注入(提供set方法)
    private String host;
    public void setHost(String host) {
        this.host = host;
    }

    // 基本类型Integer   set注入(提供set方法)
    private Integer port;
    public void setPort(Integer port) {
        this.port = port;
    }

    // List集合  set注入(提供set方法)
    public List<String> list;
    public void setList(List<String> list) {
        this.list = list;
    }
    // List集合输出
    public void printList() {
        list.forEach(s -> System.out.println(s));
    }

    // Set集合  set注入(提供set方法)
    private Set<String> set;
    public void setSet(Set<String> set) {
        this.set = set;
    }
    // Set集合输出
    public void printSet() {
        set.forEach(s -> System.out.println(s));
    }


    // Map set注入(提供set方法)
    private Map<String,Object> map;
    public void setMap(Map<String, Object> map) {
        this.map = map;
    }
    // Map输出
    public void printMap() {
        map.forEach((k,v) -> System.out.println(k + "," + v));
    }


    // Properties set注入(提供set方法)
    private Properties properties;
    public void setProperties(Properties properties) {
        this.properties = properties;
    }
    // Properties输出
    public  void printProperties(){
        properties.forEach((k,v) -> System.out.println(k + ","+ v ));
    }



    public  void  test(){
        System.out.println("UserService Test...");

        userDao.test();

        studentDao.test();

        System.out.println("Host:" + host  + ",port:" + port);

        // List集合
        printList();

        // Set集合
        printSet();

        // Map
        printMap();

        // Properties
        printProperties();

    }
}
2、配置文件的bean标签设置property标签
<!--
        IOC通过property标签手动装配(注入):
            Set方法注入
                name:bean对象中属性字段的名称
                ref:指定bean标签的id属性值
                value:具体的值(基本类型 常用对象|日期  集合)

    -->
    <bean id="userService" class="com.shsxt.service.UserService">
        <!--业务对象 注入-->
        <property name="userDao" ref="userDao"/>
        <property name="studentDao" ref="studentDao"/>

        <!--常用对象String 注入-->
        <property name="host" value="192.168.1.109"/>
        <!--基本类型注入-->
        <property name="port" value="8080"/>

        <!--List集合 注入-->
        <property name="list">
            <list>
                <value>上海</value>
                <value>北京</value>
                <value>杭州</value>
            </list>
        </property>

        <!--Set集合注入-->
        <property name="set">
            <set>
                <value>上海SH</value>
                <value>北京BJ</value>
                <value>杭州HZ</value>
            </set>
        </property>

        <!--Map注入-->
        <property name="map">
            <map>
                <entry>
                    <key><value>周杰伦</value></key>
                    <value>我是如此相信</value>
                </entry>
                <entry>
                    <key><value>林俊杰</value></key>
                    <value>可惜没如果</value>
                </entry>
                <entry>
                    <key><value>陈奕迅</value></key>
                    <value>十年</value>
                </entry>
            </map>
        </property>

        <!--Properties注入-->
        <property name="properties">
            <props>
                <prop key="上海">东方明珠</prop>
                <prop key="北京">天安门</prop>
                <prop key="杭州">西湖</prop>
            </props>
        </property>

    </bean>
构造器注入

Java代码:

提供带参构造器

public class UserService02 {

    private UserDao02 userDao02;
    /*public UserService02(UserDao02 userDao02) {
        this.userDao02 = userDao02;
    }*/


    private StudentDao02 studentDao02;
    /*public UserService02(UserDao02 userDao02, StudentDao02 studentDao02) {
        this.userDao02 = userDao02;
        this.studentDao02 = studentDao02;
    }*/

    private String uname;
    public UserService02(UserDao02 userDao02, StudentDao02 studentDao02, String uname) {
        this.userDao02 = userDao02;
        this.studentDao02 = studentDao02;
        this.uname = uname;
    }

    public  void  test(){
        System.out.println("UserService02 Test...");

        userDao02.test();

        studentDao02.test();

        System.out.println(uname);
    }

}

XML配置:

 <!--
        IOC通过构造器注入:
            通过constructor-arg标签进行注入
                name:属性名称
                ref:指定bean标签的id属性值
                value:基本类型 常用对象的值
                index:构造器中参数的下标,从0开始
    -->
    <bean id="userService02" class="com.shsxt.service.UserService02">
        <constructor-arg name="userDao02" ref="userDao02"></constructor-arg>
        <constructor-arg name="studentDao02" ref="studentDao02"></constructor-arg>
        <constructor-arg name="uname" value="admin"></constructor-arg>
    </bean>

    <bean id="userDao02" class="com.shsxt.dao.UserDao02" ></bean>
    <bean id="studentDao02" class="com.shsxt.dao.StudentDao02" ></bean>

可能出现的问题:循环依赖

Java代码:

public class AccountService {

    private RoleService roleService;

   public AccountService(RoleService roleService) {
        this.roleService = roleService;
    }

    public void  test() {
        System.out.println("AccountService Test...");
    }
}

public class RoleService {

    private AccountService accountService;

   public RoleService(AccountService accountService) {
        this.accountService = accountService;
    }

    public void  test() {
        System.out.println("RoleService Test...");
    }
}

XML配置:

 <!--
        如果多个bean对象中互相注入,则会出现循环依赖的问题
        可以通过set方法注入解决
    -->
<bean id="accountService" class="com.shsxt.service.AccountService">
    <constructor-arg name="roleService" ref="roleService"/>
</bean>

<bean id="roleService" class="com.shsxt.service.RoleService">
    <constructor-arg name="accountService" ref="accountService"/>
</bean>

如何解决:将构造器注入改为set方法注入

public class AccountService {

    private RoleService roleService;

   /* public AccountService(RoleService roleService) {
        this.roleService = roleService;
    }*/

    public void setRoleService(RoleService roleService) {
        this.roleService = roleService;
    }

    public void  test() {
        System.out.println("AccountService Test...");
    }
}

public class RoleService {

    private AccountService accountService;

   /* public RoleService(AccountService accountService) {
        this.accountService = accountService;
    }*/

    public void setAccountService(AccountService accountService) {
        this.accountService = accountService;
    }

    public void  test() {
        System.out.println("RoleService Test...");
    }
}
<!--修改为set方法注入-->
    <bean id="accountService" class="com.shsxt.service.AccountService">
        <property name="roleService" ref="roleService"/>
    </bean>

    <bean id="roleService" class="com.shsxt.service.RoleService">
       <property name="accountService" ref="accountService"/>
    </bean>
静态工厂注入(了解)

1、定义工厂类

public class StaticFactory {

    // 定义静态方法
    public static TeacherService createTeacherService() {
        return new TeacherService();
    }
}


2、在配置文件中设置bean标签,指定工厂对象并设置对应的方法

<!--静态工厂注入-->
   <bean id="teacherService" class="com.shsxt.factory.StaticFactory" factory-method="createTeacherService"></bean>

实例化工厂注入(了解)

1、定义工厂类

public class InstanceFactory {
    public TeacherService createService() {
        return new TeacherService();
    }
}

2、声明工厂bean标签,声明bean对象,指明工厂对象和工厂方法

 <!--实例化工厂注入-->
<bean id="instanceFactory" class="com.shsxt.factory.InstanceFactory"></bean>
<bean id="teacherService" factory-bean="instanceFactory" factory-method="createService"></bean>

Spring IOC 自动装配(注入)

1、准备环境(修改配置文件)

<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
       http://www.springframework.org/schema/context/spring-context.xsd">

2、开启自动化注入

<!--开启自动化装配(注入)-->
<context:annotation-config/>

3、给注入的bean对象添加注解

@Resource注解
@Resource注解实现自动注入(反射)
1、默认根据属性字段名称查找对应的bean对象 (属性字段的名称与bean标签的id属性值相等)
2、如果属性字段名称未找到,则会通过类型(Class类型)查找
3、属性可以提供set方法,也可以不提供set方法
4、注解可以声明在属性级别 或 set方法级别
5、@Resource可以设置name属性,name属性值必须与bean的id属性值一致;如果设置了name属性值,就只会按照name属性值查找bean对象
6、当注入接口时,如果接口只有一个实现则正常实例化;如果接口存在多个实现,则需要使用name属性指定需要被实例化的bean对象


private UserDao ud;
@Resource
public void setUd(UserDao ud) {
    this.ud = ud;
}

@Resource(name = "userDao03")
private IUserDao iUserDao;
@AutoWired注解
@Autowired注解实现自动化注入:
  1、默认通过类型(Class类型)查找bean对象   与属性字段的名称无关
  2、属性可以提供set方法,也可以不提供set方法
  3、注解可以声明在属性级别 或 set方法级别
  4、可以添加@Qualifier结合使用,通过value属性值查找beam对象(value属性值必须要设置,且值要与bwan标签的id属性值对应)


@Autowired
@Qualifier(value = "accountDao")
private AccountDao accountDao;

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

Spring Ioc扫描器

Spring IOC扫描器
    作用:bean对象统一进行管理,简化开发配置,提高开发效率
    1、设置自动化扫描的范围
             如果bean对象未在指定包范围,即使声明另外注解,也无法实例化
    2、使用指定的注解(声明在类级别)    bean对象的id属性默认是 类的首字母小写
          Dao层:
             @Repository
          Service层:
             @Service
          Controller层:
             @Controller
          任意类:
             @Component
     注:开发过程中建议按照指定规则声明注解

1、设置自动化扫描的范围

 <!-- 设置自动化扫描的范围 -->
 <context:component-scan base-package="com.shsxt"/>

2、使用特定的注解

@Controller
public class ResourceController {

    @Autowired
    private ResourceService resourceService;

    public  void  test() {
        System.out.println("ResourceController...");
        resourceService.test();
    }
}
@Service
public class ResourceService {

    @Resource
    private ResourceDao resourceDao;

    public  void  test() {
        System.out.println("ResourceService...");
        resourceDao.test();
    }
}
@Repository
public class ResourceDao {

    public void  test() {
        System.out.println("ResourceDao...");
    }
}
@Component
public class PropertyUtils {
    public void test(){
        System.out.println("PropertyUtils...");
    }
}

Bean的作用域

​ singleton作用域

​ prototype作用域

​ web作用域

Bean的作用域
    scope:
         设置bean对象的作用域(singleton或prototype),默认singleton
    singleton单例作用域
          Spring IOC容器在实例化对象时,会将单例对象设置到单例缓存池中

         lazy-init属性(懒加载)
                 如果为false,则在IOC容器启动时会实例化bean对象,默认false
                 如果为true,则IOC容器启动时不会实例化Bean对象,在使用bean对象时才会实例化

         lazy-init设置为false有什么好处?
                 1、可以提前发现潜在的配置问题
                 2、Bean 对象存在于缓存中,使用时不用再去实例化bean,加快程序运行效率

         什么对象适合作为单例对象?
                 无状态的对象 (不存在会改变对象状态的成员变量)
                         controller层、service层、dao层

  prototype原型作用域
          Spring IOC容器在实例化对象时,不会将单例对象设置到单例缓存池中,每一次实例都是一个新的对象

  Web作用域
         request作用域
                 在每一次请求实例化一个bean对象
         session作用域
                 在每一次有效会话实例化一个bean对象
         globalSession作用域
                 类似于session

Bean的生命周期

Bean的生命周期
        Bean的生命周期包括Bean的定义、初始化、使用和销毁4个阶段
        定义
                通过配置文档的方式来定义Bean的
        初始化
                默认在IOC容器加载时,就实例化对象
                Spring bean 初始化有两种方式:
                        I.在配置文档中通过指定 init-method 属性来完成。
                        II.实现org.springframework.beans.factory.InitializingBean接口。
        使用
                I.使用BeanFactory
                II.使用ApplicationContext
        销毁
                步骤一:实现销毁方式(Spring容器会维护bean对象的管理,可以指定bean对象的销毁所要执行的方法)
                        <bean id="userServiceImpl"class="com.shsxt.service.impl.UserServiceImpl" init-method="init" destroy-method="destroy">
                        </bean>

                步骤二:过AbstractApplicationContext 对象,调用其close方法实现bean的销毁过程。
                        AbstractApplicationContext ctx=new ClassPathXmlApplicationContext("Spring-application.xml");
                        ctx.close();

IOC-控制反转和依赖注入
      将对象实例化的创建过程转交给外部容器(IOC容器 充当工厂角色)去负责;属性赋值的操作;

Spring第三天

Spring AOP 面向切面编程

一、代理模式

​ 为某一个对象(委托类)提供一个代理(代理类),用来控制对这个对象的访问。委托类和代理类有一个共同繁荣父类或父接口。代理类会对请求做预处理、过滤,将请求分配给指定对象。

​ 生活中常见的代理情况:

​ 租房中介、婚庆公司等

​ 代理模式的两个设计原则:

​ 1. 代理类 与 委托类 具有相似的行为(共同)

​ 2. 代理类增强委托类的行为

​ 常用的代理模式:

​ 静态代理 和 动态代理

二、静态代理

1、静态代理的三要素(婚庆公司)

​ a、有共同的行为(结婚) - 接口

​ b、目标角色(新郎) - 实现行为

​ c、代理角色(婚庆公司) - 实现行为 增强目标对象行为

2、静态代理的特点

​ 1、在应用程序执行前就得到目标角色

​ 2、代理对象会增强目标对象的行为

​ 3、目标角色固定

​ 4、有可能存在多个代理 引起"类爆炸"(缺点)

3、静态代理的实现
1. 定义行为(共同) 定义接口
/**
 * 定义行为
 */
public interface Marry {
    public void toMarry();
}
2. 目标对象(实现行为)
/**
 * 静态代理 ——> 目标对象
 */
public class You implements  Marry {
    // 实现行为
    @Override
    public void toMarry() {
        System.out.println("我要结婚了...");
    }
}
3. 代理对象(实现行为、增强目标对象的行为)
/**
 * 静态代理 ——> 代理对象
 */
public class MarryCompanyProxy implements Marry {

    // 目标对象
    private Marry marry;
    // 通过构造器将目标对象传入
    public MarryCompanyProxy(Marry marry) {
        this.marry = marry;
    }

    // 实现行为
    @Override
    public void toMarry() {
        // 增强行为
        before();
                
        // 执行目标对象中的方法
        marry.toMarry();
        
        // 增强行为
        after();
    }

    /**
     * 增强行为
     */
    private void after() {
        System.out.println("新婚快乐,早生贵子!");
    }

    /**
     * 增强行为
     */
    private void before() {
        System.out.println("场地正在布置中...");
    }
}
4. 通过代理对象实现目标对象的功能
// 目标对象
You you = new You();
// 代理对象
MarryCompanyProxy marryCompanyProxy = new MarryCompanyProxy(you);
// 通过代理对象调用目标对象中的方法
marryCompanyProxy.toMarry();

三、动态代理

1. 动态代理的特点

​ 1、在应用程序执行时动态创建目标对象

​ 2、代理对象会增强目标对象的行为

​ 3、目标对象不固定

注:JDK动态代理的目标对象必须有接口实现

2. JDK动态代理
  • newProxyInstance
    /*
    	返回一个指定接口的代理类的实例方法调用分派到指定的调用处理程序。 (返回代理对象)
    	loader:类加载器
    	interfaces:对象实现的接口数组
    	h:InvocationHandler接口
    */
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
    
    获取代理对象
    public class JdkHandler implements InvocationHandler {
    
        // 目标对象
        private Object target; // 目标对象的类型不固定,创建时动态生成
        // 通过构造器将目标对象赋值
        public JdkHandler(Object target) {
            this.target = target;
        }
    
        /**
         *  1、调用目标对象的方法(返回Object)
         *  2、增强目标对象的行为
         * @param proxy 代理类的实例对象
         * @param method  目标对象的方法
         * @param args  目标对象的方法形参
         * @return
         * @throws Throwable
         */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
            // 增强行为
            System.out.println("==============方法前执行");
    
            // 调用目标对象的方法(返回Object)
            Object result = method.invoke(target,args);
    
            // 增强行为
            System.out.println("方法后执行==============");
    
            return result;
        }
    
    
        /**
         * 得到代理对象
         * public static Object newProxyInstance(ClassLoader loader,
         *                                       Class<?>[] interfaces,
         *                                       InvocationHandler h)
         *      loader:类加载器
         * 	    interfaces:对象实现的接口数组
         * 	    h:InvocationHandler接口
         *
         *
         * @return
         */
        public Object getProxy() {
            return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
        }
    }
    
    
    通过代理对象实现目标对象的功能
    // 目标对象
    You you = new You();
    // 获取代理对象
    JdkHandler jdkHandler = new JdkHandler(you);
    Marry marry = (Marry) jdkHandler.getProxy();
    // 通过代理对象调用目标对象中的方法
    marry.toMarry();
    
3. CGLIB动态代理

​ 动态代理:

​ 继承思想

1、在pom.xml文件中引入cglib的相关依赖

<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>2.2.2</version>
</dependency>

2、定义类,实现MethodInterceptor接口


public class CglibInterceptor implements MethodInterceptor {

    // 目标对象
    private Object target;
    // 通过构造器传入目标对象
    public CglibInterceptor(Object target) {
        this.target = target;
    }

    /**
     * 获取代理对象
     * @return
     */
    public Object getProxy() {
        // 通过Enhancer对象的create()方法可以生成一个类
        Enhancer enhancer = new Enhancer();
        // 设置父类 (将目标类作为其父类)
        enhancer.setSuperclass(target.getClass());
        // 设置回调,调用回调对象中的
        enhancer.setCallback(this);
        return enhancer.create();
    }

    /**
     * 1、目标对象的方法调用
     * 2、增强行为
     * @param o
     * @param method
     * @param objects
     * @param methodProxy
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {

        // 增强行为
        System.out.println("==============方法前执行");

        // 调用目标对象的方法(返回Object)
        Object result = methodProxy.invoke(target,objects);

        // 增强行为
        System.out.println("方法后执行==============");

        return result;
    }

}

3、调用方法

// 目标对象
You you = new You();
CglibInterceptor cglibInterceptor = new CglibInterceptor(you);
Marry marry = (Marry) cglibInterceptor.getProxy();
marry.toMarry();

User user = new User();
CglibInterceptor cglibInterceptor = new CglibInterceptor(user);
User u = (User) cglibInterceptor.getProxy();
u.test();
4. JDK动态代理 和 Cglib动态代理
1. JDK动态代理实现接口,Cglib动态代理继承思想
2. JDK动态代理(目标对象存在接口时)执行效率高于Ciglib
3. 如如果目标对象有接口实现,选择JDK代理,如果没有接口实现选择Cglib代理

四、Spring AOP

需要对以下功能进行日志记录,事务处理

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SlqWWS31-1581090934907)(F:/SHSXT/Java%E9%AB%98%E7%BA%A7/%E7%AC%94%E8%AE%B0/Java33/images/1576770254892.png)]

假如没有AOP,在做日志和事务处理的时候,我们会在每个方法中添加相关处理

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uogt8l9v-1581090934908)(F:/SHSXT/Java%E9%AB%98%E7%BA%A7/%E7%AC%94%E8%AE%B0/Java33/images/1576769002873.png)]

但大多数的代码是相同的,为了实现代码复用,我们可能把日志和事务处理抽离成一个新的方法。但是这样我们仍然必须手动插入这些方法。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6rnuoLu7-1581090934910)(F:/SHSXT/Java%E9%AB%98%E7%BA%A7/%E7%AC%94%E8%AE%B0/Java33/images/1576769398313.png)]

但这样两个方法就是强耦合的,假如此时我们不需要这个功能了,或者想换成其他功能,那么就必须一个个修改。

通过代理模式,可以在指定位置执行对应流程。这样就可以将一些横向的功能抽离出来形成一个独立的模块,然后在指定位置插入这些功能。这样的思想,被称为面向切面编程,即AOP。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4DBoqNhD-1581090934911)(F:/SHSXT/Java%E9%AB%98%E7%BA%A7/%E7%AC%94%E8%AE%B0/Java33/images/1576767721274.png)]

1. 什么是AOP?

​ AOP:Aspect Oriented Programming 面向切面编程

​ AOP关注不是某一个类或某些方法;控制大量资源,关注的是大量的类和方法。

2. AOP能做什么?

​ AOP主要应用于日志记录,性能统计,安全控制,事务处理等方面,实现公共功能性的重复使用。

3. AOP的特点

​ 1. 降低模块与模块之间的耦合度,提高业务代码的聚合度。(高内聚低耦合)

​ 2. 提高了代码的复用性。

​ 3. 提高系统的扩展性。(高版本兼容低版本)

​ 4. 可以在不影响原有的功能基础上添加新的功能

4. AOP的底层实现

​ 动态代理(JDK + CGLIB)

5. AOP基本概念
1. Joinpoint(连接点)

​ 匹配到的每一个方法,spring aop一个连接点即代表一个方法的执行

2. Pointcut(切入点)

​ 匹配规则。规定什么方法被拦截、需要处理什么方法

3. Advice(通知)

​ 拦截到每一个方法(每一个连接点)所执行的操作

​ 前置通知:在方法被执行之前通知

​ 最终通知(finally):无论方法执行过程中是否出现异常,最后都会通知

​ 返回通知:当方法(无异常)执行完毕后通知

​ 异常通知:当方法执行时出现异常通知

​ 环绕通知:可以替代上面四种 有返回值的通知

4. Aspect(切面)

​ 切入点和通知的抽象 (与面向对象中的 类 相似)

​ 定义 切入点和通知 (切入点定义了要拦截哪些类的哪些方法,通知则定义了拦截过方法后要做什么)

5. Target(目标对象)

​ 被代理的目标对象

6. Weave(织入)

​ 将切面应用到目标对象并生成代理对象的这个过程即为织入

7. Introduction(引入)

​ 在不修改原有应用程序代码的情况下,在程序运行期为类动态添加方法或者字段的过程称为引入

五、Spring AOP实现

1. Spring AOP环境搭建

a、jar 包坐标引入

<!--Spring AOP-->
<dependency>
	<groupId>org.aspectj</groupId>
	<artifactId>aspectjweaver</artifactId>
	<version>1.8.9</version>
</dependency>

b、添加spring.xml的配置

添加命名空间

xmlns:aop="http://www.springframework.org/schema/aop"
 http://www.springframework.org/schema/aop
 http://www.springframework.org/schema/aop/spring-aop.xsd

2. 注解实现

  1. 定义切面

/**
 * 切面
 *  切入点和通知的抽象   (与面向对象中的 类 相似)
 * 	定义 切入点和通知 (切入点定义了要拦截哪些类的哪些方法,通知则定义了拦截过方法后要做什么)
 */
@Component // 将对象交给IOC容器去实例化
@Aspect // 声明当前类是一个切面
public class LogCut {

    /**
     *  切入点:
     *      匹配规则。规定什么方法被拦截、需要处理什么方法
     *  定义切入点
     *    @Pointcut("匹配规则")
     *  语法:
     *    @Pointcut("execution (* com.shsxt.service..*.*(..) )")
     *    第一个*  代表的是方法的修饰范围 (private、protected、public) *表示所有范围
     *    com.shsxt.service..*.*(..)
     *       匹配com.shsxt.service包下及子包下所有的类的所有方法
     *    com.shsxt.service.*.*(..)
     *       匹配com.shsxt.service包下所有的类的所有方法
     *
     */
    @Pointcut("execution (* com.shsxt.service..*.*(..) )")
    public void cut(){}

    /**
     * 声明前置通知   并将通知应用到定义的切入点上
     * 目标类方法执行前 执行该通知
     *
     */
   @Before(value = "cut()")
    public void before() {
        System.out.println("前置通知.....");
    }

    /**
     * 声明返回通知   并将通知应用到定义的切入点上
     * 目标类方法(无异常)执行后 执行该通知
     *
     */
   @AfterReturning(value = "cut()")
    public void afterReturn() {
        System.out.println("返回通知.....");
    }

    /**
     * 声明最终通知   并将通知应用到定义的切入点上
     * 目标类方法(无异常或有异常)执行后 执行该通知
     *
     */
    @After(value = "cut()")
    public void after() {
        System.out.println("最终通知.....");
    }

    /**
     * 声明异常通知   并将通知应用到定义的切入点上
     * 目标类方法出现异常时 执行该通知
     */
    @AfterThrowing(value="cut()",throwing = "e")
    public void afterThrow(Exception e) {
        System.out.println("异常通知....." + "  异常原因:" + e.getCause());
    }

    /**
     *  声明环绕通知  并将通知应用到切入点上
     *  方法执行前后  通过环绕通知定义相应处理
     *      需要通过显式调用对应的方法,否则无法访问指定方法 (pjp.proceed();)
     * @param pjp
     * @return
     */
    @Around(value = "cut()")
    public Object around(ProceedingJoinPoint pjp) {
        System.out.println("前置通知...");

        Object object = null;
        try {
            object = pjp.proceed();
            System.out.println(pjp.getTarget() + "======" + pjp.getSignature());
            // System.out.println("返回通知...");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            System.out.println("异常通知...");
        }
        System.out.println("最终通知...");

        return object;
    }

}
  1. 配置文件(spring.xml)
 <!--配置AOP代理-->
 <aop:aspectj-autoproxy/>

3. XML实现

  1. 定义切面
**
 * 切面
 *  切入点和通知的抽象   (与面向对象中的 类 相似)
 * 	定义 切入点和通知 (切入点定义了要拦截哪些类的哪些方法,通知则定义了拦截过方法后要做什么)
 */
@Component // 将对象交给IOC容器去实例化
public class LogCut02 {


    public void cut(){}


    /**
     * 声明前置通知   并将通知应用到定义的切入点上
     * 目标类方法执行前 执行该通知
     *
     */

    public void before() {
        System.out.println("前置通知.....");
    }

    /**
     * 声明返回通知   并将通知应用到定义的切入点上
     * 目标类方法(无异常)执行后 执行该通知
     *
     */

    public void afterReturn() {
        System.out.println("返回通知.....");
    }

    /**
     * 声明最终通知   并将通知应用到定义的切入点上
     * 目标类方法(无异常或有异常)执行后 执行该通知
     *
     */

    public void after() {
        System.out.println("最终通知.....");
    }

    /**
     * 声明异常通知   并将通知应用到定义的切入点上
     * 目标类方法出现异常时 执行该通知
     */

    public void afterThrow(Exception e) {
        System.out.println("异常通知....." + "  异常原因:" + e.getCause());
    }


    /**
     *  声明环绕通知  并将通知应用到切入点上
     *  方法执行前后  通过环绕通知定义相应处理
     *      需要通过显式调用对应的方法,否则无法访问指定方法 (pjp.proceed();)
     * @param pjp
     * @return
     */

    public Object around(ProceedingJoinPoint pjp) {
        System.out.println("前置通知...");

        Object object = null;
        try {
            object = pjp.proceed();
            System.out.println(pjp.getTarget() + "======" + pjp.getSignature());
            // System.out.println("返回通知...");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            System.out.println("异常通知...");
        }
        System.out.println("最终通知...");

        return object;
    }

}
  1. 配置文件(spring.xml)
 <!--aop相关配置-->
<aop:config>
    <!--aop切面-->
    <aop:aspect ref="logCut02">
        <!-- 定义aop 切入点 -->
        <aop:pointcut id="cut" expression="execution(* com.shsxt.service..*.*(..))"/>
        <!-- 配置前置通知 指定前置通知方法名  并引用切入点定义 -->
        <aop:before method="before" pointcut-ref="cut"/>
        <!-- 配置返回通知 指定返回通知方法名  并引用切入点定义 -->
        <aop:after-returning method="afterReturn" pointcut-ref="cut"/>
        <!-- 配置异常通知  指定异常通知方法名  并引用切入点定义 -->
        <aop:after-throwing method="afterThrow" throwing="e" pointcut-ref="cut"/>
        <!-- 配置最终通知  指定最终通知方法名  并引用切入点定义 -->
        <aop:after method="after" pointcut-ref="cut"/>
        <!-- 配置环绕通知  指定环绕通知方法名  并引用切入点定义 -->
        <aop:around method="around" pointcut-ref="cut"/>
    </aop:aspect>
</aop:config>

Spring第四天

Spring JDBC

1. SpringJDBC 环境搭建

1.1 引入依赖(添加坐标)
<!--junit单元测试-->
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
</dependency>
<!-- spring 测试环境 -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>4.3.2.RELEASE</version>
    <scope>test</scope>
</dependency>

<!-- spring 框架坐标依赖添加 -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>4.3.2.RELEASE</version>
</dependency>

<!-- aop -->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.8.9</version>
</dependency>

<!-- mysql 驱动包 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.39</version>
</dependency>
<!-- c3p0 连接池 -->
<dependency>
    <groupId>c3p0</groupId>
    <artifactId>c3p0</artifactId>
    <version>0.9.1.2</version>
</dependency>

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

<!-- spring事物 -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-tx</artifactId>
    <version>4.3.2.RELEASE</version>
</dependency>
1.2 添加db.properties文件
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring_jdbc?useUnicode=true&characterEncoding=utf8
jdbc.user=root
jdbc.password=root
1.3 添加spring.xml文件
  1. 设置扫描器范围
  2. 加载properties 配置文件
  3. 配置成c3p0 数据源
  4. 配置JdbcTemplate 模板
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       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
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd">

    <!--设置扫描器范围-->
    <context:component-scan base-package="com.shsxt"/>

    <!-- 加载properties 配置文件 -->
    <context:property-placeholder location="db.properties"/>

    <!-- 配置c3p0 数据源 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driver}"/>
        <property name="jdbcUrl" value="${jdbc.url}"/>
        <property name="user" value="${jdbc.user}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

    <!-- jdbcTemplate 配置 -->
    <bean id="jdbcTemplate"  class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

</beans>
1.4 测试JDBC
@Test
public void testQueryCount() {

    // 得到Spring上下文环境
    ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml");
    // 得到模板类 JdbcTemplate对象
    JdbcTemplate jdbcTemplate = (JdbcTemplate) ac.getBean("jdbcTemplate");

    Integer count = jdbcTemplate.queryForObject("select count(1) from account",Integer.class);
    Integer count1 = jdbcTemplate.queryForObject("select count(1) from account where userId = ?",Integer.class,1);

    System.out.println(count+"-------"+count1);
}

简易版:

使用注解方式

@RunWith(SpringJUnit4ClassRunner.class)

​ 将junit测试加到spring环境中
@ContextConfiguration(locations = {“classpath:spring.xml”})

​ 设置要加载的资源文件

  1. 定义父类
@RunWith(SpringJUnit4ClassRunner.class) // 将junit测试加到spring环境中
@ContextConfiguration(locations = {"classpath:spring.xml"}) // 设置要加载的资源文件
public class TestBase {

}
  1. 继承父类
public class SpringJdbcTest extends TestBase {

    @Resource
    private JdbcTemplate jdbcTemplate;

    @Test
    public void testQueryCount() {

        Integer count = jdbcTemplate.queryForObject("select count(1) from account",Integer.class);
        Integer count1 = jdbcTemplate.queryForObject("select count(1) from account where userId = ?",Integer.class,1);

        System.out.println(count+"-------"+count1);
}
 <property name="jdbcUrl" value="${jdbc.url}"/>
    <property name="user" value="${jdbc.user}"/>
    <property name="password" value="${jdbc.password}"/>
</bean>

<!-- jdbcTemplate 配置 -->
<bean id="jdbcTemplate"  class="org.springframework.jdbc.core.JdbcTemplate">
    <property name="dataSource" ref="dataSource"></property>
</bean>
```
1.4 测试JDBC
@Test
public void testQueryCount() {

    // 得到Spring上下文环境
    ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml");
    // 得到模板类 JdbcTemplate对象
    JdbcTemplate jdbcTemplate = (JdbcTemplate) ac.getBean("jdbcTemplate");

    Integer count = jdbcTemplate.queryForObject("select count(1) from account",Integer.class);
    Integer count1 = jdbcTemplate.queryForObject("select count(1) from account where userId = ?",Integer.class,1);

    System.out.println(count+"-------"+count1);
}

简易版:

使用注解方式

@RunWith(SpringJUnit4ClassRunner.class)

​ 将junit测试加到spring环境中
@ContextConfiguration(locations = {“classpath:spring.xml”})

​ 设置要加载的资源文件

  1. 定义父类
@RunWith(SpringJUnit4ClassRunner.class) // 将junit测试加到spring环境中
@ContextConfiguration(locations = {"classpath:spring.xml"}) // 设置要加载的资源文件
public class TestBase {

}
  1. 继承父类
public class SpringJdbcTest extends TestBase {

    @Resource
    private JdbcTemplate jdbcTemplate;

    @Test
    public void testQueryCount() {

        Integer count = jdbcTemplate.queryForObject("select count(1) from account",Integer.class);
        Integer count1 = jdbcTemplate.queryForObject("select count(1) from account where userId = ?",Integer.class,1);

        System.out.println(count+"-------"+count1);
}
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值