【Spring】SpringIOC容器的配置及使用

文章目录

  • spring初识
      • 1、框架
      • 2、spring
      • 3、IOC(Inversion of Control):控制反转
      • 4、总结
  • 02spring IOC基本使用
      • 1、spring_helloworld
          • **(1)使用手动加载jar包的方式实现,分为三个步骤,现在几乎不用**
          • **(2)使用maven的方式来构建项目**
      • 2、spring对象的获取及属性赋值方式
          • **1、通过bean的id获取IOC容器中的对象(上面已经用过)**
          • **2、通过bean的类型获取对象**
          • **3、通过构造器给bean对象赋值**
          • **4、通过命名空间为bean赋值,简化配置文件中属性声明的写法**
          • **5、为复杂类型进行赋值操作**
          • 6、继承关系bean的配置
          • 7、bean对象创建的依赖关系
          • 8、bean的作用域控制,是否是单例
          • 9、利用工厂模式创建bean对象
          • 10、继承FactoryBean来创建对象
          • 11、bean对象的初始化和销毁方法
          • 12、配置bean对象初始化方法的前后处理方法
      • 3、spring创建第三方bean对象
      • 4、spring引用外部配置文件
      • 5、spring基于xml文件的自动装配
      • 6、SpEL的使用
  • 03SpringIOC的注解应用
      • 1、使用注解的方式注册bean到IOC容器中
      • 2、定义扫描包时要包含的类和不要包含的类
      • 3、使用@AutoWired进行自动注入
      • 4、@AutoWired可以进行定义在方法上
      • 5、自动装配的注解@AutoWired,@Resource
      • 6、泛型依赖注入
  • 04Spring AOP介绍与使用
      • 1、AOP的概念
          • 为什么要引入AOP?
          • AOP的核心概念及术语
          • AOP的通知类型
          • AOP的应用场景
      • 2、Spring AOP的简单配置
          • 1、添加pom依赖
          • 2、编写配置
          • 3、测试
          • 4、通过cglib来创建代理对象
          • 注意:
            • 1、切入点表达式
            • 2、通知方法的执行顺序
            • 3、获取方法的详细信息
            • 4、spring对通过方法的要求
            • 5、表达式的抽取
            • 6、环绕通知的使用
            • 7、多切面运行的顺序
      • 3、基于配置的AOP配置
  • maven的介绍与使用
      • 1、maven的简单介绍
      • 2、maven的安装
      • 3、maven的基本常识
      • 4、maven常用命令

名词 概念
EJB EJB是Enterprise Java Beans技术的简称, 又被称为企业Java Beans,是基于分布式事务处理的企业级应用程序的组件
SSH Secure Shell,SSH 为建立在应用层基础上的安全协议,SSH 是较可靠,专为远程登录会话和其他网络服务提供安全性的协议
SSM 框架集:ssm全称Spring+SpringMVC+MyBatis,是目前比较主流的Java EE企业级框架,适用于搭建各种大型的企业级应用系统。
spring boot Spring是一个开源框架,spring mvc < spring
apache dubbo 高性能Java RPC(remote procedure call,远程调用过程为RPC)框架
AJAX 全称:Asynchronous JavaScript And XML(异步 JavaScript 及 XML)Ajax的作用:实现异步请求的技术。在不刷新页面(使用a标签发送请求和使用form表单发送请求,这两种请求都会,刷新页面)的情况下,发送请求,接收响应,然后修改部分的页面,这样的需求需要异步请求实现。

框架是什么什么是框架?为什么要使用框架?框架特点

代码模板化。每个框架都有自己的使用规范,如:创建类、接口等的规范 重用性、通用性。不分行业、不分业务,只要功能相似就可以稍加修改即可使用
高内聚(封装)。各种基础的功能都封装好了,只需要在使用的时候调用就可以。无需关注底层实现原理。 可扩展、可维护

spring初识

1、框架

​ 框架就是一些类和接口的集合,通过这些类和接口协调来完成一系列的程序实现。JAVA框架可以分为三层:表示层,业务层和物理层。框架又叫做开发中的半成品,它不能提供整个WEB应用程序的所有东西,但是有了框架,我们就可以集中精力进行业务逻辑的开发而不用去关心它的技术实现以及一些辅助的业务逻辑。大家熟知的Structs和Spring就是表示层和业务层框架的强力代表。(说的太官方了)

​ 人话:

​ 框架就是某些个人或者组织定义了一系列的类或者接口,提前定义好了一些实现,用户可以在这些类和接口的基础之上,使用这些类来迅速的形成某个领域或者某个行业的解决方案,简化开发的过程,提高开发的效率。就好比:你要盖一座房子,先把柱子,房梁等先建设好,然后只需要向房子中填充就可以了,可以按照自己的需求进行设计,其实我们做的项目、系统都是类似的方式,如果所有的代码全部都需要自己实现,那么这个工程就太庞大了,所以可以先创建出一些基础的模板框架,开发人员只需要按照自己的需求向架子中填充内容,完成自己独特需求即可,这就是框架存在的意义。其实我们之前定义的简单的工具类这些东西也是类似的原理,只不过比较单一简单而已,因此,在现在的很多项目系统开发的过程中都是利用框架进行开发。

2、spring

架构设计

​ 随着互联网的发展,网站应用的规模不断扩大,常规的垂直应用架构已无法应对,分布式服务架构以及流动计算架构势在必行,亟需一个治理系统确保架构有条不紊的演进。

【Spring】SpringIOC容器的配置及使用_第1张图片

​ 单一应用架构

​ 当网站流量很小时,只需一个应用,将所有功能都部署在一起,以减少部署节点和成本。此时,用于简化增删改查工作量的数据访问框架(ORM)是关键。

​ 垂直应用架构

​ 当访问量逐渐增大,单一应用增加机器带来的加速度越来越小,提升效率的方法之一是将应用拆成互不相干的几个应用,以提升效率。此时,用于加速前端页面开发的Web框架(MVC)是关键。

​ 分布式服务架构

​ 当垂直应用越来越多,应用之间交互不可避免,将核心业务抽取出来,作为独立的服务,逐渐形成稳定的服务中心,使前端应用能更快速的响应多变的市场需求。此时,用于提高业务复用及整合的分布式服务框架(RPC)是关键。

​ 流动计算架构

​ 当服务越来越多,容量的评估,小服务资源的浪费等问题逐渐显现,此时需增加一个调度中心基于访问压力实时管理集群容量,提高集群利用率。此时,用于提高机器利用率的资源调度和治理中心(SOA)是关键。

Java主流框架演变之路

​ 1、JSP+Servlet+JavaBean

​ 2、MVC三层架构

【Spring】SpringIOC容器的配置及使用_第2张图片

​ 3、使用EJB进行应用的开发,但是EJB是重量级框架(在使用的时候,过多的接口和依赖,侵入性强),在使用上比较麻烦

​ 4、Struts1/Struts2+Hibernate+Spring

​ 5、SpringMVC+Mybatis+Spring

​ 6、SpringBoot开发,约定大于配置

Spring官网

官网地址:https://spring.io/projects/spring-framework#overview

压缩包下载地址:https://repo.spring.io/release/org/springframework/spring/

源码地址:https://github.com/spring-projects/spring-framework

核心解释

​ spring是一个开源框架。

​ spring是为了简化企业开发而生的,使得开发变得更加优雅和简洁。

​ spring是一个IOCAOP的容器框架。

​ IOC:控制反转

​ AOP:面向切面编程

​ 容器:包含并管理应用对象的生命周期,就好比用桶装水一样,spring就是桶,而对象就是水

使用spring的优点

​ 1、Spring通过DI、AOP和消除样板式代码来简化企业级Java开发

​ 2、Spring框架之外还存在一个构建在核心框架之上的庞大生态圈,它将Spring扩展到不同的领域,如Web服务、REST、移动开发以及NoSQL

​ 3、低侵入式设计,代码的污染极低

​ 4、独立于各种应用服务器,基于Spring框架的应用,可以真正实现Write Once,Run Anywhere的承诺

​ 5、Spring的IoC容器降低了业务对象替换的复杂性,提高了组件之间的解耦

​ 6、Spring的AOP支持允许将一些通用任务如安全、事务、日志等进行集中式处理,从而提供了更好的复用

​ 7、Spring的ORM和DAO提供了与第三方持久层框架的的良好整合,并简化了底层的数据库访问

​ 8、Spring的高度开放性,并不强制应用完全依赖于Spring,开发者可自由选用Spring框架的部分或全部

如何简化开发

​ 基于POJO的轻量级和最小侵入性编程

​ 通过依赖注入和面向接口实现松耦合

​ 基于切面和惯例进行声明式编程

​ 通过切面和模板减少样板式代码

spring的模块划分图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zWnbU0wg-1642387534544)(…\spring\image\spring-overview.png)]

模块解释:
Test:Spring的单元测试模块
Core Container:核心容器模块
AOP+Aspects:面向切面编程模块
Instrumentation:提供了class instrumentation支持和类加载器的实现来在特定的应用服务器上使用,几乎不用
Messaging:包括一系列的用来映射消息到方法的注解,几乎不用
Data Access/Integration:数据的获取/整合模块,包括了JDBC,ORM,OXM,JMS和事务模块
Web:提供面向web整合特性

3、IOC(Inversion of Control):控制反转

需要导入四个基础包
为什么要引入IOC

创建一个普通的java项目,完成下述功能

UserDao.java

package com.mashibing.dao;

public interface UserDao {
    public void getUser();
}

UserDaoImpl.java

package com.mashibing.dao.impl;

import com.mashibing.dao.UserDao;

public class UserDaoImpl  implements UserDao {
    @Override
    public void getUser() {
        System.out.println("获取用户数据");
    }
}

UserService.java

package com.mashibing.service;

public interface UserService {
    public void getUser();
}

UserServiceImpl.java

package com.mashibing.service.impl;

import com.mashibing.dao.UserDao;
import com.mashibing.dao.impl.UserDaoImpl;
import com.mashibing.dao.impl.UserDaoMysqlImpl;
import com.mashibing.service.UserService;

public class UserServiceImpl implements UserService {

    private UserDao userDao = new UserDaoImpl();

    @Override
    public void getUser() {
        userDao.getUser();
    }
}

SpringDemoTest.java

package com.mashibing.test;

import com.mashibing.service.UserService;
import com.mashibing.service.impl.UserServiceImpl;

public class SpringDemoTest {
    public static void main(String[] args) {
       UserService service = new UserServiceImpl();
       service.getUser();
    }
}

在之前的代码编写过程中,我们都是这么完成我们的功能的,但是如果增加一个UserDao的实现类呢?

UserDaoMysqlImpl.java

package com.mashibing.dao.impl;

import com.mashibing.dao.UserDao;

public class UserDaoMysqlImpl implements UserDao {
    @Override
    public void getUser() {
        System.out.println("mysql");
    }
}

如果我们想要使用mysql的话,那么就必须要修改UserServiceImpl.java的代码:

package com.mashibing.service.impl;

import com.mashibing.dao.UserDao;
import com.mashibing.dao.impl.UserDaoImpl;
import com.mashibing.dao.impl.UserDaoMysqlImpl;
import com.mashibing.service.UserService;

public class UserServiceImpl implements UserService {

    private UserDao userDao = new UserDaoImpl();

    @Override
    public void getUser() {
        userDao.getUser();
    }
}

但是如果我们再增加一个oracle的类呢?

UserDaoOracleImpl.java

package com.mashibing.dao.impl;

import com.mashibing.dao.UserDao;

public class UserDaoOracleImpl implements UserDao {
    @Override
    public void getUser() {
        System.out.println("oracle");
    }
}

此时UserService还是要继续修改,很显然这样的方式已经不适用于我们的需求了,那么怎么解决呢,可以使用如下的方式

UserServiceImpl.java

package com.mashibing.service.impl;

import com.mashibing.dao.UserDao;
import com.mashibing.dao.impl.UserDaoImpl;
import com.mashibing.service.UserService;

public class UserServiceImpl implements UserService {
    private UserDao userDao;

    public void setUserDao(UserDao userDao){
        this.userDao = userDao;
    }
    @Override
    public void getUser() {
        userDao.getUser();
    }
}

测试类SpringDemoTest.java

package com.mashibing.test;

import com.mashibing.dao.impl.UserDaoMysqlImpl;
import com.mashibing.dao.impl.UserDaoOracleImpl;
import com.mashibing.service.UserService;
import com.mashibing.service.impl.UserServiceImpl;

public class SpringDemoTest {
    public static void main(String[] args) {
        UserServiceImpl userService = new UserServiceImpl();
        userService.setUserDao(new UserDaoMysqlImpl());
        userService.getUser();

        userService.setUserDao(new UserDaoOracleImpl());
        userService.getUser();
    }
}

其实从刚刚的代码中,大家应该能体会解耦的重要性了,下面我们就开始学习Spring的IOC。

IOC初始

​ 想要搞明白IOC,那么需要搞清楚如下几个问题:

1、谁控制谁
2、控制什么
3、什么是反转
4、哪些方面被反转

基本概念

	IoC is also known as dependency injection (DI). It is a process whereby objects define their dependencies (that is, the other objects they work with) only through constructor arguments, arguments to a factory method, or properties that are set on the object instance after it is constructed or returned from a factory method. The container then injects those dependencies when it creates the bean. This process is fundamentally the inverse (hence the name, Inversion of Control) of the bean itself controlling the instantiation or location of its dependencies by using direct construction of classes or a mechanism such as the Service Locator pattern.
	IOC与大家熟知的依赖注入同理,这是一个通过依赖注入对象的过程 也就是说,它们所使用的对象,是通过构造函数参数,工厂方法的参数或这是从工厂方法的构造函数或返回值的对象实例设置的属性,然后容器在创建bean时注入这些需要的依赖。 这个过程相对普通创建对象的过程是反向的(因此称之为IoC),bean本身通过直接构造类来控制依赖关系的实例化或位置,或提供诸如服务定位器模式之类的机制。

如果这个过程比较难理解的话,那么可以想象自己找女朋友和婚介公司找女朋友的过程。如果这个过程能够想明白的话,那么我们现在回答上面的问题:

1、谁控制谁:在之前的编码过程中,都是需要什么对象自己去创建什么对象,有程序员自己来控制对象,而有了IOC容器之后,就会变成由IOC容器来控制对象,
2、控制什么:在实现过程中所需要的对象及需要依赖的对象
3、什么是反转:在没有IOC容器之前我们都是在对象中主动去创建依赖的对象,这是正转的,而有了IOC之后,依赖的对象直接由IOC容器创建后注入到对象中,由主动创建变成了被动接受,这是反转
4、哪些方面被反转:依赖的对象

DI与IOC

很多人把IOC和DI说成一个东西,笼统来说的话是没有问题的,但是本质上还是有所区别的,希望大家能够严谨一点,IOC和DI是从不同的角度描述的同一件事,IOC是从容器的角度描述,而DI是从应用程序的角度来描述,也可以这样说,IOC是设计思想,而DI是具体的实现方式

4、总结

​ 在此处总结中,希望大家能够能够明白两件事:

1、解耦

​ 在面向对象设计的软件系统中,底层的实现都是由N个对象组成的,所有的对象通过彼此的合作,最终实现系统的业务逻辑。
【Spring】SpringIOC容器的配置及使用_第3张图片

​ 需要注意的是,在这样的组合关系中,一旦某一个对象出现了问题,那么其他对象肯定回有所影响,这就是耦合性太高的缘故,但是对象的耦合关系是无法避免的,也是必要的。随着应用程序越来越庞大,对象的耦合关系可能越来越复杂,经常需要多重依赖关系,因此,无论是架构师还是程序员,在面临这样的场景的时候,都需要减少这些对象的耦合性。

【Spring】SpringIOC容器的配置及使用_第4张图片

​ 耦合的关系不仅仅是对象与对象之间,也会出现在软件系统的各个模块之间,是我们需要重点解决的问题。而为了解决对象之间的耦合度过高的问题,我们就可以通过IOC来实现对象之间的解耦,spring框架就是IOC理论最最广泛的应用。

【Spring】SpringIOC容器的配置及使用_第5张图片

​ 从上图中可以看到,当引入了第三方的容器之后,几个对象之间就没有了耦合关系,全部对象都交由容器来控制,这个容器就相当于粘合剂,将系统的对象粘合在一起发挥作用。

2、生态

​ 任何一个语言或者任何一个框架想要立于不败之地,那么很重要的就是它的生态。

02spring IOC基本使用

​ 通过前面的介绍我们已经知道了Spring中非常重要的一个特性就是IOC,下面我们将要来看一下如何使用IOC容器,帮助大家更好的体会spring的优势。

1、spring_helloworld

(1)使用手动加载jar包的方式实现,分为三个步骤,现在几乎不用

【Spring】SpringIOC容器的配置及使用_第6张图片
从下往上core container就是IOC

  • 导包:导入这五个包即可

    commons-logging-1.2.jar
    spring-beans-5.2.3.RELEASE.jar
    spring-context-5.2.3.RELEASE.jar
    spring-core-5.2.3.RELEASE.jar
    spring-expression-5.2.3.RELEASE.jar

  • 写配置

    Person.java

    package com.mashibing.bean;
    
    public class Person {
        private int id;
        private String name;
        private int age;
        private String gender;
    
        public int getId() {
            return id;
        }
    
        public void setId(int id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        public String getGender() {
            return gender;
        }
    
        public void setGender(String gender) {
            this.gender = gender;
        }
    
        @Override
        public String toString() {
            return "Person{" +
                    "id=" + id +
                    ", name='" + name + '\'' +
                    ", age=" + age +
                    ", gender='" + gender + '\'' +
                    '}';
        }
    }
    
    

    ioc.xml

    
    <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">
    
        
        
        <bean id="person" class="com.mashibing.bean.Person">
            
            <property name="id" value="1">property>
            <property name="name" value="zhangsan">property>
            <property name="age" value="18">property>
            <property name="gender" value="">property>
        bean>
    beans>
    
  • 测试

SpringDemoTest.java

//容器中的person对象是什么时候创建的?
//容器中的对象在容器创建完成之前就已经把对象创建好了
public class MyTest {
    public static void main(String[] args) {
        /*
        * applicationContext:表示IOC容器的入口,想要获取对象的话,必须要创建该类
        *   该类有两个读取配置文件的实现类
        *       ClassPathXmlApplicationContext:表示从classpath中读取数据
        *       FileSystemXmlApplicationContext:表示从当前文件系统读取数据
        *
        *
        * * */
        ApplicationContext context = new ClassPathXmlApplicationContext("ioc.xml");
        //获取具体的bean实例对象,需要进行强制类型转换
//        Person person = (Person) context.getBean("person");
        //获取对象的时候不需要强制类型转换
//        Person person = context.getBean("person", Person.class);
//        System.out.println(person);
    }
}

package com.mashibing.test;

import com.mashibing.bean.Person;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringDemoTest {
    public static void main(String[] args) {
        //ApplicationContext:表示ioc容器
        //ClassPathXmlApplicationContext:表示从当前classpath路径中获取xml文件的配置
        //根据spring的配置文件来获取ioc容器对象
        ApplicationContext context = new ClassPathXmlApplicationContext("ioc.xml");
        Person person = (Person) context.getBean("person");
        System.out.println(person);
    }
}
(2)使用maven的方式来构建项目
  • 创建maven项目

    定义项目的groupId、artifactId

  • 添加对应的pom依赖

    pom.xml

    
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0modelVersion>
    
        <groupId>com.mashibinggroupId>
        <artifactId>spring_demoartifactId>
        <version>1.0-SNAPSHOTversion>
    
        <dependencies>
            
            <dependency>
                <groupId>org.springframeworkgroupId>
                <artifactId>spring-contextartifactId>
                <version>5.2.3.RELEASEversion>
            dependency>
        dependencies>
    project>
    
  • 编写代码

    Person.java

    package com.mashibing.bean;
    public class Person {
        private int id;
        private String name;
        private int age;
        private String gender;
    
        public int getId() {
            return id;
        }
    
        public void setId(int id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        public String getGender() {
            return gender;
        }
    
        public void setGender(String gender) {
            this.gender = gender;
        }
    
        @Override
        public String toString() {
            return "Person{" +
                    "id=" + id +
                    ", name='" + name + '\'' +
                    ", age=" + age +
                    ", gender='" + gender + '\'' +
                    '}';
        }
    }
    
  • 测试

    MyTest.java

import com.mashibing.bean.Person;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("ioc.xml");
        Person person = (Person) context.getBean("person");
        System.out.println(person);
    }
}

总结:

​ 以上两种方式创建spring的项目都是可以的,但是在现在的企业开发环境中使用更多的还是maven这样的方式,无须自己处理jar之间的依赖关系,也无须提前下载jar包,只需要配置相关的pom即可,因此推荐大家使用maven的方式,具体的maven操作大家可以看maven的详细操作文档。

搭建spring项目需要注意的点:

​ 1、一定要将配置文件添加到类路径中,使用idea创建项目的时候要放在resource目录下

​ 2、导包的时候别忘了commons-logging-1.2.jar包

细节点:

​ 1、ApplicationContext就是IOC容器的接口,可以通过此对象获取容器中创建的对象

​ 2、对象在Spring容器创建完成的时候就已经创建完成,不是需要用的时候才创建

​ 3、对象在IOC容器中存储的时候都是单例的,如果需要多例需要修改属性

​ 4、创建对象给属性赋值的时候是通过setter方法实现的

​ 5、对象的属性是由setter/getter方法决定的,而不是定义的成员属性

2、spring对象的获取及属性赋值方式

1、通过bean的id获取IOC容器中的对象(上面已经用过)
2、通过bean的类型获取对象

​ MyTest.java

import com.mashibing.bean.Person;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("ioc.xml");
        Person bean = context.getBean(Person.class);
        System.out.println(bean);
    }
}

注意:通过bean的类型在查找对象的时候,在配置文件中不能存在两个类型一致的bean对象,如果有的话,可以通过如下方法

MyTest.java

import com.mashibing.bean.Person;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("ioc.xml");
        Person person = context.getBean("person", Person.class);
        System.out.println(person);
    }
}
3、通过构造器给bean对象赋值

ioc.xml

	
	<bean id="person2" class="com.mashibing.bean.Person">
        <constructor-arg name="id" value="1">constructor-arg>
        <constructor-arg name="name" value="lisi">constructor-arg>
        <constructor-arg name="age" value="20">constructor-arg>
        <constructor-arg name="gender" value="">constructor-arg>
    bean>

	
	<bean id="person3" class="com.mashibing.bean.Person">
        <constructor-arg value="1">constructor-arg>
        <constructor-arg value="lisi">constructor-arg>
        <constructor-arg value="20">constructor-arg>
        <constructor-arg value="">constructor-arg>
    bean>

	
    <bean id="person4" class="com.mashibing.bean.Person">
        <constructor-arg value="lisi" index="1">constructor-arg>
        <constructor-arg value="1" index="0">constructor-arg>
        <constructor-arg value="" index="3">constructor-arg>
        <constructor-arg value="20" index="2">constructor-arg>
    bean>
	
	将person的age类型设置为Integer类型
	public Person(int id, String name, Integer age) {
        this.id = id;
        this.name = name;
        this.age = age;
        System.out.println("Age");
    }

    public Person(int id, String name, String gender) {
        this.id = id;
        this.name = name;
        this.gender = gender;
        System.out.println("gender");
    }
	<bean id="person5" class="com.mashibing.bean.Person">
        <constructor-arg value="1">constructor-arg>
        <constructor-arg value="lisi">constructor-arg>
        <constructor-arg value="20" type="java.lang.Integer">constructor-arg>
    bean>
	
	 <bean id="person5" class="com.mashibing.bean.Person">
        <constructor-arg value="1">constructor-arg>
        <constructor-arg value="lisi">constructor-arg>
        <constructor-arg value="20" type="int" index="2">constructor-arg>
    bean>
4、通过命名空间为bean赋值,简化配置文件中属性声明的写法

​ 1、导入命名空间


<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

​ 2、添加配置

    <bean id="person6" class="com.mashibing.bean.Person" p:id="3" p:name="wangwu" p:age="22" p:gender="">bean>
5、为复杂类型进行赋值操作

​ 在之前的测试代码中,我们都是给最基本的属性进行赋值操作,在正常的企业级开发中还会遇到给各种复杂类型赋值,如集合、数组、其他对象等。

​ Person.java

package com.mashibing.bean;

import java.util.*;

public class Person {
    private int id;
    private String name="dahuang";
    private int age;
    private String gender;
    private Address address;
    private String[] hobbies;
    private List<Book> books;
    private Set<Integer> sets;
    private Map<String,Object> maps;
    private Properties properties;

    public Person(int id, String name, int age, String gender) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.gender = gender;
        System.out.println("有参构造器");
    }

    public Person(int id, String name, int age) {
        this.id = id;
        this.name = name;
        this.age = age;
        System.out.println("Age");
    }

    public Person(int id, String name, String gender) {
        this.id = id;
        this.name = name;
        this.gender = gender;
        System.out.println("gender");
    }

    public Person() {
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    public List<Book> getBooks() {
        return books;
    }

    public void setBooks(List<Book> books) {
        this.books = books;
    }

    public Map<String, Object> getMaps() {
        return maps;
    }

    public void setMaps(Map<String, Object> maps) {
        this.maps = maps;
    }

    public Properties getProperties() {
        return properties;
    }

    public void setProperties(Properties properties) {
        this.properties = properties;
    }

    public String[] getHobbies() {
        return hobbies;
    }

    public void setHobbies(String[] hobbies) {
        this.hobbies = hobbies;
    }

    public Set<Integer> getSets() {
        return sets;
    }

    public void setSets(Set<Integer> sets) {
        this.sets = sets;
    }

    @Override
    public String toString() {
        return "Person{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", gender='" + gender + '\'' +
                ", address=" + address +
                ", hobbies=" + Arrays.toString(hobbies) +
                ", books=" + books +
                ", sets=" + sets +
                ", maps=" + maps +
                ", properties=" + properties +
                '}';
    }
}


Book.java

package com.mashibing.bean;

public class Book {
    private String name;
    private String author;
    private double price;

    public Book() {
    }

    public Book(String name, String author, double price) {
        this.name = name;
        this.author = author;
        this.price = price;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    @Override
    public String toString() {
        return "Book{" +
                "name='" + name + '\'' +
                ", author='" + author + '\'' +
                ", price=" + price +
                '}';
    }
}

Address.java

package com.mashibing.bean;

public class Address {
    private String province;
    private String city;
    private String town;

    public Address() {
    }

    public Address(String province, String city, String town) {
        this.province = province;
        this.city = city;
        this.town = town;
    }

    public String getProvince() {
        return province;
    }

    public void setProvince(String province) {
        this.province = province;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public String getTown() {
        return town;
    }

    public void setTown(String town) {
        this.town = town;
    }

    @Override
    public String toString() {
        return "Address{" +
                "province='" + province + '\'' +
                ", city='" + city + '\'' +
                ", town='" + town + '\'' +
                '}';
    }
}

ioc.xml


<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd"
>

    
    <bean id="person" class="com.mashibing.bean.Person">
        <property name="name">
            
            <null>null>
        property>
        
        <property name="address" ref="address">property>
        
       
        
        <property name="books">
            <list>
                
                <bean id="book1" class="com.mashibing.bean.Book">
                    <property name="name" value="多线程与高并发">property>
                    <property name="author" value="马士兵">property>
                    <property name="price" value="1000">property>
                bean>
                
                <ref bean="book2">ref>
            list>
        property>
        
        <property name="maps" ref="myMap">property>
        
        <property name="properties">
            <props>
                <prop key="aaa">aaaprop>
                <prop key="bbb">222prop>
            props>
        property>
        
        <property name="hobbies">
            <array>
                <value>bookvalue>
                <value>movievalue>
                <value>gamevalue>
            array>
        property>
        
        <property name="sets">
            <set>
                <value>111value>
                <value>222value>
                <value>222value>
            set>
        property>
    bean>
    <bean id="address" class="com.mashibing.bean.Address">
        <property name="province" value="河北">property>
        <property name="city" value="邯郸">property>
        <property name="town" value="武安">property>
    bean>
    <bean id="book2" class="com.mashibing.bean.Book">
        <property name="name" value="JVM">property>
        <property name="author" value="马士兵">property>
        <property name="price" value="1200">property>
    bean>
    
    <bean id="person2" class="com.mashibing.bean.Person">
        <property name="address" ref="address">property>
        <property name="address.province" value="北京">property>
    bean>
    
    <util:map id="myMap">
            <entry key="key1" value="value1">entry>
            <entry key="key2" value-ref="book2">entry>
            <entry key="key03">
                <bean class="com.mashibing.bean.Book">
                    <property name="name" value="西游记" >property>
                    <property name="author" value="吴承恩" >property>
                    <property name="price" value="100" >property>
                bean>
            entry>
    util:map>
beans>
6、继承关系bean的配置

ioc.xml

    <bean id="person" class="com.mashibing.bean.Person">
        <property name="id" value="1">property>
        <property name="name" value="zhangsan">property>
        <property name="age" value="21">property>
        <property name="gender" value="">property>
    bean>
    
    <bean id="person2" class="com.mashibing.bean.Person" parent="person">
        <property name="name" value="lisi">property>
    bean>

如果想实现Java文件的抽象类,不需要将当前bean实例化的话,可以使用abstract属性

 	<bean id="person" class="com.mashibing.bean.Person" abstract="true">
        <property name="id" value="1">property>
        <property name="name" value="zhangsan">property>
        <property name="age" value="21">property>
        <property name="gender" value="">property>
    bean>
    
    <bean id="person2" class="com.mashibing.bean.Person" parent="person">
        <property name="name" value="lisi">property>
    bean>
7、bean对象创建的依赖关系

​ bean对象在创建的时候是按照bean在配置文件的顺序决定的,也可以使用depend-on标签来决定顺序

ioc.xml

	<bean id="book" class="com.mashibing.bean.Book" depends-on="person,address">bean>
    <bean id="address" class="com.mashibing.bean.Address">bean>
    <bean id="person" class="com.mashibing.bean.Person">bean>
8、bean的作用域控制,是否是单例

ioc.xml

    
    <bean id="person4" class="com.mashibing.bean.Person" scope="prototype">bean>

设计模式:单例模式、多例模式、工厂模式等,抽丝剥茧设计模式,设计模式之禅

9、利用工厂模式创建bean对象

​ 在之前的案例中,所有bean对象的创建都是通过反射得到对应的bean实例,其实在spring中还包含另外一种创建bean实例的方式,就是通过工厂模式进行对象的创建

​ 在利用工厂模式创建bean实例的时候有两种方式,分别是静态工厂和实例工厂。

​ 静态工厂:工厂本身不需要创建对象,但是可以通过静态方法调用,对象=工厂类.静态工厂方法名();

​ 实例工厂:工厂本身需要创建对象,工厂类 工厂对象=new 工厂类;工厂对象.get对象名();

PersonStaticFactory.java

package com.mashibing.factory;

import com.mashibing.bean.Person;

public class PersonStaticFactory {

    public static Person getPerson(String name){
        Person person = new Person();
        person.setId(1);
        person.setName(name);
        return person;
    }
}

ioc.xml


<bean id="person5" class="com.mashibing.factory.PersonStaticFactory" factory-method="getPerson">
        
        <constructor-arg value="lisi">constructor-arg>
    bean>

PersonInstanceFactory.java

package com.mashibing.factory;

import com.mashibing.bean.Person;

public class PersonInstanceFactory {
    public Person getPerson(String name){
        Person person = new Person();
        person.setId(1);
        person.setName(name);
        return person;
    }
}

ioc.xml

    
    
    <bean id="personInstanceFactory" class="com.mashibing.factory.PersonInstanceFactory">bean>
    
    <bean id="person6" class="com.mashibing.bean.Person" factory-bean="personInstanceFactory" factory-method="getPerson">
        <constructor-arg value="wangwu">constructor-arg>
    bean>
10、继承FactoryBean来创建对象

​ FactoryBean是Spring规定的一个接口,当前接口的实现类,Spring都会将其作为一个工厂,但是在ioc容器启动的时候不会创建实例,只有在使用的时候才会创建对象

MyFactoryBean.java

package com.mashibing.factory;

import com.mashibing.bean.Person;
import org.springframework.beans.factory.FactoryBean;

/**
 * 实现了FactoryBean接口的类是Spring中可以识别的工厂类,spring会自动调用工厂方法创建实例
 */
public class MyFactoryBean implements FactoryBean<Person> {

    /**
     * 工厂方法,返回需要创建的对象
     * @return
     * @throws Exception
     */
    @Override
    public Person getObject() throws Exception {
        Person person = new Person();
        person.setName("maliu");
        return person;
    }

    /**
     * 返回创建对象的类型,spring会自动调用该方法返回对象的类型
     * @return
     */
    @Override
    public Class<?> getObjectType() {
        return Person.class;
    }

    /**
     * 创建的对象是否是单例对象
     * @return
     */
    @Override
    public boolean isSingleton() {
        return false;
    }
}

ioc.xml

<bean id="myfactorybean" class="com.mashibing.factory.MyFactoryBean">bean>
11、bean对象的初始化和销毁方法

​ 在创建对象的时候,我们可以根据需要调用初始化和销毁的方法

Address.java

package com.mashibing.bean;

public class Address {
    private String province;
    private String city;
    private String town;

    public Address() {
        System.out.println("address被创建了");
    }

    public Address(String province, String city, String town) {
        this.province = province;
        this.city = city;
        this.town = town;
    }

    public String getProvince() {
        return province;
    }

    public void setProvince(String province) {
        this.province = province;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public String getTown() {
        return town;
    }

    public void setTown(String town) {
        this.town = town;
    }

    public void init(){
        System.out.println("对象被初始化");
    }
    
    public void destory(){
        System.out.println("对象被销毁");
    }
    
    @Override
    public String toString() {
        return "Address{" +
                "province='" + province + '\'' +
                ", city='" + city + '\'' +
                ", town='" + town + '\'' +
                '}';
    }
}

ioc.xml


    <bean id="address" class="com.mashibing.bean.Address" init-method="init" destroy-method="destory">bean>

MyTest.java

import com.mashibing.bean.Address;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("ioc2.xml");
        Address address = context.getBean("address", Address.class);
        System.out.println(address);
        //applicationContext没有close方法,需要使用具体的子类
        ((ClassPathXmlApplicationContext)context).close();

    }
}
12、配置bean对象初始化方法的前后处理方法

​ spring中包含一个BeanPostProcessor的接口,可以在bean的初始化方法的前后调用该方法,如果配置了初始化方法的前置和后置处理器,无论是否包含初始化方法,都会进行调用

MyBeanPostProcessor.java

package com.mashibing.bean;

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

public class MyBeanPostProcessor implements BeanPostProcessor {
    /**
     * 在初始化方法调用之前执行
     * @param bean  初始化的bean对象
     * @param beanName  xml配置文件中的bean的id属性
     * @return
     * @throws BeansException
     */
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("postProcessBeforeInitialization:"+beanName+"调用初始化前置方法");
        return bean;
    }

    /**
     * 在初始化方法调用之后执行
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("postProcessAfterInitialization:"+beanName+"调用初始化后缀方法");
        return bean;
    }
}

ioc.xml

<bean id="myBeanPostProcessor" class="com.mashibing.bean.MyBeanPostProcessor">bean>

3、spring创建第三方bean对象

​ 在Spring中,很多对象都是单实例的,在日常的开发中,我们经常需要使用某些外部的单实例对象,例如数据库连接池,下面我们来讲解下如何在spring中创建第三方bean实例。

​ 1、导入数据库连接池的pom文件


<dependency>
    <groupId>com.alibabagroupId>
    <artifactId>druidartifactId>
    <version>1.1.21version>
dependency>

<dependency>
    <groupId>mysqlgroupId>
    <artifactId>mysql-connector-javaartifactId>
    <version>5.1.47version>
dependency>

​ 2、编写配置文件

ioc.xml


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

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

​ 3、编写测试文件

MyTest.java

import com.alibaba.druid.pool.DruidDataSource;
import com.mashibing.bean.Address;
import com.mashibing.bean.Person;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.sql.SQLException;

public class MyTest {
    public static void main(String[] args) throws SQLException {
        ApplicationContext context = new ClassPathXmlApplicationContext("ioc3.xml");
        DruidDataSource dataSource = context.getBean("dataSource", DruidDataSource.class);
        System.out.println(dataSource);
        System.out.println(dataSource.getConnection());
    }
}

4、spring引用外部配置文件

在resource中添加dbconfig.properties

username=root
password=123456
url=jdbc:mysql://localhost:3306/demo
driverClassName=com.mysql.jdbc.Driver

编写配置文件


<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
        http://www.springframework.org/schema/context/spring-context.xsd">
	
    <context:property-placeholder location="classpath:dbconfig.properties"/>
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="username" value="${username}">property>
        <property name="password" value="${password}">property>
        <property name="url" value="${url}">property>
        <property name="driverClassName" value="${driverClassName}">property>
    bean>
beans>

5、spring基于xml文件的自动装配

​ 当一个对象中需要引用另外一个对象的时候,在之前的配置中我们都是通过property标签来进行手动配置的,其实在spring中还提供了一个非常强大的功能就是自动装配,可以按照我们指定的规则进行配置,配置的方式有以下几种:

​ default/no:不自动装配

​ byName:按照名字进行装配,以(set后面的名字)属性名作为id去容器中查找组件,进行赋值,如果找不到则装配null

​ byType:按照类型进行装配,以属性的类型作为查找依据去容器中找到这个组件,如果有多个类型相同的bean对象,那么会报异常,如果找不到则装配null

​ constructor:按照构造器进行装配,先按照有参构造器参数的类型进行装配,没有就直接装配null;如果按照类型找到了多个,那么就使用参数名作为id继续匹配,找到就装配,找不到就装配null

ioc.xml


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

    <bean id="address" class="com.mashibing.bean.Address">
        <property name="province" value="河北">property>
        <property name="city" value="邯郸">property>
        <property name="town" value="武安">property>
    bean>
    <bean id="person" class="com.mashibing.bean.Person" autowire="byName">bean>
    <bean id="person2" class="com.mashibing.bean.Person" autowire="byType">bean>
    <bean id="person3" class="com.mashibing.bean.Person" autowire="constructor">bean>
beans>

6、SpEL的使用

​ SpEL:Spring Expression Language,spring的表达式语言,支持运行时查询操作对象

​ 使用#{…}作为语法规则,所有的大括号中的字符都认为是SpEL.

ioc.xml

    <bean id="person4" class="com.mashibing.bean.Person">
        
        <property name="age" value="#{12*2}">property>
        
        <property name="name" value="#{address.province}">property>
        
        <property name="address" value="#{address}">property>
        
        <property name="hobbies" value="#{T(java.util.UUID).randomUUID().toString().substring(0,4)}">property>
        
        <property name="gender" value="#{address.getCity()}">property>
    bean>

03SpringIOC的注解应用

​ 在之前的项目中,我们都是通过xml文件进行bean或者某些属性的赋值,其实还有另外一种注解的方式,在企业开发中使用的很多,在bean上添加注解,可以快速的将bean注册到ioc容器。

1、使用注解的方式注册bean到IOC容器中

applicationContext.xml


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

    
    
    <context:component-scan base-package="com.mashibing">context:component-scan>
beans>

PersonController.java

package com.mashibing.controller;

import org.springframework.stereotype.Controller;

@Controller
public class PersonController {
    public PersonController() {
        System.out.println("创建对象");
    }
}

PersonService.java

package com.mashibing.service;

import org.springframework.stereotype.Service;

@Service
public class PersonService {
}

PersonDao.java

package com.mashibing.dao;

import org.springframework.stereotype.Repository;

@Repository("personDao")
@Scope(value="prototype")
public class PersonDao {
}

2、定义扫描包时要包含的类和不要包含的类

​ 当定义好基础的扫描包后,在某些情况下可能要有选择性的配置是否要注册bean到IOC容器中,此时可以通过如下的方式进行配置。

applicationContext.xml


<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
        http://www.springframework.org/schema/context/spring-context.xsd">
    <context:component-scan base-package="com.mashibing" use-default-filters="false">
        


        
        <context:include-filter type="assignable" expression="com.mashibing.service.PersonService"/>
    context:component-scan>
beans>

3、使用@AutoWired进行自动注入

​ 使用注解的方式实现自动注入需要使用@AutoWired注解。

PersonController.java

package com.mashibing.controller;

import com.mashibing.service.PersonService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

@Controller
public class PersonController {

    @Autowired
    private PersonService personService;

    public PersonController() {
        System.out.println("创建对象");
    }

    public void getPerson(){
        personService.getPerson();
    }
}

PersonService.java

package com.mashibing.service;

import com.mashibing.dao.PersonDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class PersonService {

    @Autowired
    private PersonDao personDao;

    public void getPerson(){
        personDao.getPerson();
    }
}

PersonDao.java

package com.mashibing.dao;

        import org.springframework.stereotype.Repository;

@Repository
public class PersonDao {

    public void getPerson(){
        System.out.println("PersonDao:getPerson");
    }
}

注意:当使用AutoWired注解的时候,自动装配的时候是根据类型实现的。

​ 1、如果只找到一个,则直接进行赋值,

​ 2、如果没有找到,则直接抛出异常,

​ 3、如果找到多个,那么会按照变量名作为id继续匹配,

​ 1、匹配上直接进行装配

​ 2、如果匹配不上则直接报异常

PersonServiceExt.java

package com.mashibing.service;

import com.mashibing.dao.PersonDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class PersonServiceExt extends PersonService{

    @Autowired
    private PersonDao personDao;

    public void getPerson(){
        System.out.println("PersonServiceExt......");
        personDao.getPerson();
    }
}

PersonController.java

package com.mashibing.controller;

import com.mashibing.service.PersonService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

@Controller
public class PersonController {

    @Autowired
    private PersonService personServiceExt;

    public PersonController() {
        System.out.println("创建对象");
    }

    public void getPerson(){
        personServiceExt.getPerson();
    }
}

​ 还可以使用@Qualifier注解来指定id的名称,让spring不要使用变量名,当使用@Qualifier注解的时候也会有两种情况:

​ 1、找到,则直接装配

​ 2、找不到,就会报错

PersonController.java

package com.mashibing.controller;

import com.mashibing.service.PersonService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;

@Controller
public class PersonController {

    @Autowired
    @Qualifier("personService")
    private PersonService personServiceExt2;

    public PersonController() {
        System.out.println("创建对象");
    }

    public void getPerson(){
        personServiceExt2.getPerson();
    }
}

​ 通过上述的代码我们能够发现,使用@AutoWired肯定是能够装配上的,如果装配不上就会报错。

4、@AutoWired可以进行定义在方法上

​ 当我们查看@AutoWired注解的源码的时候发现,此注解不仅可以使用在成员变量上,也可以使用在方法上。

PersonController.java

package com.mashibing.controller;

import com.mashibing.dao.PersonDao;
import com.mashibing.service.PersonService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;

@Controller
public class PersonController {

    @Qualifier("personService")
    @Autowired
    private PersonService personServiceExt2;

    public PersonController() {
        System.out.println("创建对象");
    }

    public void getPerson(){
        System.out.println("personController..."+personServiceExt2);
//        personServiceExt2.getPerson();
    }

     /**
     * 当方法上有@AutoWired注解时:
     *  1、此方法在bean创建的时候会自动调用
     *  2、这个方法的每一个参数都会自动注入值
     * @param personDao
     */
    @Autowired
    public void test(PersonDao personDao){
        System.out.println("此方法被调用:"+personDao);
    }
    
    /**
     * @Qualifier注解也可以作用在属性上,用来被当作id去匹配容器中的对象,如果没有
     * 此注解,那么直接按照类型进行匹配
     * @param personService
     */
    @Autowired
    public void test2(@Qualifier("personServiceExt") PersonService personService){
        System.out.println("此方法被调用:"+personService);
    }
}

5、自动装配的注解@AutoWired,@Resource

​ 在使用自动装配的时候,出了可以使用@AutoWired注解之外,还可以使用@Resource注解,大家需要知道这两个注解的区别。

​ 1、@AutoWired:是spring中提供的注解,@Resource:是jdk中定义的注解,依靠的是java的标准

​ 2、@AutoWired默认是按照类型进行装配,默认情况下要求依赖的对象必须存在,@Resource默认是按照名字进行匹配的,同时可以指定name属性。

​ 3、@AutoWired只适合spring框架,而@Resource扩展性更好

PersonController.java

package com.mashibing.controller;

import com.mashibing.dao.PersonDao;
import com.mashibing.service.PersonService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;

import javax.annotation.Resource;

@Controller
public class PersonController {

    @Qualifier("personService")
    @Resource
    private PersonService personServiceExt2;

    public PersonController() {
        System.out.println("创建对象");
    }

    public void getPerson(){
        System.out.println("personController..."+personServiceExt2);
        personServiceExt2.getPerson();
    }

    /**
     * 当方法上有@AutoWired注解时:
     *  1、此方法在bean创建的时候会自动调用
     *  2、这个方法的每一个参数都会自动注入值
     * @param personDao
     */
    @Autowired
    public void test(PersonDao personDao){
        System.out.println("此方法被调用:"+personDao);
    }

    /**
     * @Qualifier注解也可以作用在属性上,用来被当作id去匹配容器中的对象,如果没有
     * 此注解,那么直接按照类型进行匹配
     * @param personService
     */
    @Autowired
    public void test2(@Qualifier("personServiceExt") PersonService personService){
        System.out.println("此方法被调用:"+personService);
    }
}

6、泛型依赖注入

​ 为了讲解泛型依赖注入,首先我们需要先写一个基本的案例,按照我们之前学习的知识:

Student.java

package com.mashibing.bean;

public class Student {
}

Teacher.java

package com.mashibing.bean;

public class Teacher {
}

BaseDao.java

package com.mashibing.dao;

import org.springframework.stereotype.Repository;

@Repository
public abstract class BaseDao<T> {

    public abstract void save();
}

StudentDao.java

package com.mashibing.dao;

import com.mashibing.bean.Student;
import org.springframework.stereotype.Repository;

@Repository
public class StudentDao extends BaseDao<Student>{
    public void save() {
        System.out.println("保存学生");
    }
}

TeacherDao.java

package com.mashibing.dao;

import com.mashibing.bean.Teacher;
import org.springframework.stereotype.Repository;

@Repository
public class TeacherDao extends BaseDao<Teacher> {
    public void save() {
        System.out.println("保存老师");
    }
}

StudentService.java

package com.mashibing.service;

import com.mashibing.dao.StudentDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class StudentService {

    @Autowired
    private StudentDao studentDao;

    public void save(){
        studentDao.save();
    }
}

TeacherService.java

package com.mashibing.service;

import com.mashibing.dao.TeacherDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class TeacherService {
    @Autowired
    private TeacherDao teacherDao;

    public void save(){
        teacherDao.save();
    }
}

MyTest.java

import com.mashibing.service.StudentService;
import com.mashibing.service.TeacherService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import javax.sql.DataSource;
import java.sql.SQLException;

public class MyTest {
    public static void main(String[] args) throws SQLException {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        StudentService studentService = context.getBean("studentService",StudentService.class);
        studentService.save();

        TeacherService teacherService = context.getBean("teacherService",TeacherService.class);
        teacherService.save();
    }
}

​ 上述代码是我们之前的可以完成的功能,但是可以思考,Service层的代码是否能够改写:

BaseService.java

package com.mashibing.service;

import com.mashibing.dao.BaseDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

public class BaseService<T> {
    
    @Autowired
    BaseDao<T> baseDao;
    
    public void save(){
        System.out.println("自动注入的对象:"+baseDao);
        baseDao.save();
    }
}

StudentService.java

package com.mashibing.service;

import com.mashibing.bean.Student;
import com.mashibing.dao.StudentDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class StudentService extends BaseService<Student> {

}

TeacherService.java

package com.mashibing.service;

import com.mashibing.bean.Teacher;
import com.mashibing.dao.TeacherDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class TeacherService extends BaseService<Teacher>{

}

04Spring AOP介绍与使用

AOP:Aspect Oriented Programming 面向切面编程

OOP:Object Oriented Programming 面向对象编程

​ 面向切面编程:基于OOP基础之上新的编程思想,OOP面向的主要对象是类,而AOP面向的主要对象是切面,在处理日志、安全管理、事务管理等方面有非常重要的作用。AOP是Spring中重要的核心点,虽然IOC容器没有依赖AOP,但是AOP提供了非常强大的功能,用来对IOC做补充。通俗点说的话就是在程序运行期间,将某段代码动态切入指定方法指定位置进行运行的这种编程方式。

1、AOP的概念

为什么要引入AOP?

Calculator.java

package com.mashibing.inter;

public interface Calculator {

    public int add(int i,int j);

    public int sub(int i,int j);

    public int mult(int i,int j);

    public int div(int i,int j);
}

MyCalculator.java

package com.mashibing.inter;

public class MyCalculator implements Calculator {
    public int add(int i, int j) {
        int result = i + j;
        return result;
    }

    public int sub(int i, int j) {
        int result = i - j;
        return result;
    }

    public int mult(int i, int j) {
        int result = i * j;
        return result;
    }

    public int div(int i, int j) {
        int result = i / j;
        return result;
    }
}

MyTest.java

public class MyTest {
    public static void main(String[] args) throws SQLException {
        MyCalculator myCalculator = new MyCalculator();
        System.out.println(myCalculator.add(1, 2));
    }
}

​ 此代码非常简单,就是基础的javase的代码实现,此时如果需要添加日志功能应该怎么做呢,只能在每个方法中添加日志输出,同时如果需要修改的话会变得非常麻烦。

MyCalculator.java

package com.mashibing.inter;

public class MyCalculator implements Calculator {
    public int add(int i, int j) {
        System.out.println("add 方法开始执行,参数为:"+i+","+j);
        int result = i + j;
        System.out.println("add 方法开始完成结果为:"+result);
        return result;
    }

    public int sub(int i, int j) {
        System.out.println("sub 方法开始执行,参数为:"+i+","+j);
        int result = i - j;
        System.out.println("add 方法开始完成结果为:"+result);
        return result;
    }

    public int mult(int i, int j) {
        System.out.println("mult 方法开始执行,参数为:"+i+","+j);
        int result = i * j;
        System.out.println("add 方法开始完成结果为:"+result);
        return result;
    }

    public int div(int i, int j) {
        System.out.println("div 方法开始执行,参数为:"+i+","+j);
        int result = i / j;
        System.out.println("add 方法开始完成结果为:"+result);
        return result;
    }
}

​ 可以考虑将日志的处理抽象出来,变成工具类来进行实现:

LogUtil.java

package com.mashibing.util;

import java.util.Arrays;

public class LogUtil {

    public static void start(Object ... objects){
        System.out.println("XXX方法开始执行,使用的参数是:"+ Arrays.asList(objects));
    }

    public static void stop(Object ... objects){
        System.out.println("XXX方法执行结束,结果是:"+ Arrays.asList(objects));
    }
}

MyCalculator.java

package com.mashibing.inter;

import com.mashibing.util.LogUtil;

public class MyCalculator implements Calculator {
    public int add(int i, int j) {
        LogUtil.start(i,j);
        int result = i + j;
        LogUtil.stop(result);
        return result;
    }

    public int sub(int i, int j) {
        LogUtil.start(i,j);
        int result = i - j;
        LogUtil.stop(result);
        return result;
    }

    public int mult(int i, int j) {
        LogUtil.start(i,j);
        int result = i * j;
        LogUtil.stop(result);
        return result;
    }

    public int div(int i, int j) {
        LogUtil.start(i,j);
        int result = i / j;
        LogUtil.stop(result);
        return result;
    }
}

​ 按照上述方式抽象之后,代码确实简单很多,但是大家应该已经发现在输出的信息中并不包含具体的方法名称,我们更多的是想要在程序运行过程中动态的获取方法的名称及参数、结果等相关信息,此时可以通过使用动态代理的方式来进行实现。

CalculatorProxy.java

package com.mashibing.proxy;

import com.mashibing.inter.Calculator;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;

/**
 * 帮助Calculator生成代理对象的类
 */
public class CalculatorProxy {

    /**
     *
     *  为传入的参数对象创建一个动态代理对象
     * @param calculator 被代理对象
     * @return
     */
    public static Calculator getProxy(final Calculator calculator){


        //被代理对象的类加载器
        ClassLoader loader = calculator.getClass().getClassLoader();
        //被代理对象的接口
        Class<?>[] interfaces = calculator.getClass().getInterfaces();
        //方法执行器,执行被代理对象的目标方法
        InvocationHandler h = new InvocationHandler() {
            /**
             *  执行目标方法
             * @param proxy 代理对象,给jdk使用,任何时候都不要操作此对象
             * @param method 当前将要执行的目标对象的方法
             * @param args 这个方法调用时外界传入的参数值
             * @return
             * @throws Throwable
             */
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //利用反射执行目标方法,目标方法执行后的返回值
//                System.out.println("这是动态代理执行的方法");
                Object result = null;
                try {
                    System.out.println(method.getName()+"方法开始执行,参数是:"+ Arrays.asList(args));
                    result = method.invoke(calculator, args);
                    System.out.println(method.getName()+"方法执行完成,结果是:"+ result);
                } catch (Exception e) {
                    System.out.println(method.getName()+"方法出现异常:"+ e.getMessage());
                } finally {
                    System.out.println(method.getName()+"方法执行结束了......");
                }
                //将结果返回回去
                return result;
            }
        };
        Object proxy = Proxy.newProxyInstance(loader, interfaces, h);
        return (Calculator) proxy;
    }
}

​ 我们可以看到这种方式更加灵活,而且不需要在业务方法中添加额外的代码,这才是常用的方式。如果想追求完美的同学,还可以使用上述的日志工具类来完善。

LogUtil.java

package com.mashibing.util;

import java.lang.reflect.Method;
import java.util.Arrays;

public class LogUtil {

    public static void start(Method method, Object ... objects){
//        System.out.println("XXX方法开始执行,使用的参数是:"+ Arrays.asList(objects));
        System.out.println(method.getName()+"方法开始执行,参数是:"+ Arrays.asList(objects));
    }

    public static void stop(Method method,Object ... objects){
//        System.out.println("XXX方法执行结束,结果是:"+ Arrays.asList(objects));
        System.out.println(method.getName()+"方法开始执行,参数是:"+ Arrays.asList(objects));

    }

    public static void logException(Method method,Exception e){
        System.out.println(method.getName()+"方法出现异常:"+ e.getMessage());
    }
    
    public static void end(Method method){
        System.out.println(method.getName()+"方法执行结束了......");
    }
}

CalculatorProxy.java

package com.mashibing.proxy;

import com.mashibing.inter.Calculator;
import com.mashibing.util.LogUtil;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;

/**
 * 帮助Calculator生成代理对象的类
 */
public class CalculatorProxy {

    /**
     *
     *  为传入的参数对象创建一个动态代理对象
     * @param calculator 被代理对象
     * @return
     */
    public static Calculator getProxy(final Calculator calculator){


        //被代理对象的类加载器
        ClassLoader loader = calculator.getClass().getClassLoader();
        //被代理对象的接口
        Class<?>[] interfaces = calculator.getClass().getInterfaces();
        //方法执行器,执行被代理对象的目标方法
        InvocationHandler h = new InvocationHandler() {
            /**
             *  执行目标方法
             * @param proxy 代理对象,给jdk使用,任何时候都不要操作此对象
             * @param method 当前将要执行的目标对象的方法
             * @param args 这个方法调用时外界传入的参数值
             * @return
             * @throws Throwable
             */
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //利用反射执行目标方法,目标方法执行后的返回值
//                System.out.println("这是动态代理执行的方法");
                Object result = null;
                try {
                    LogUtil.start(method,args);
                    result = method.invoke(calculator, args);
                    LogUtil.stop(method,args);
                } catch (Exception e) {
                    LogUtil.logException(method,e);
                } finally {
                    LogUtil.end(method);
                }
                //将结果返回回去
                return result;
            }
        };
        Object proxy = Proxy.newProxyInstance(loader, interfaces, h);
        return (Calculator) proxy;
    }
}

​ 很多同学看到上述代码之后可能感觉已经非常完美了,但是要说明的是,这种动态代理的实现方式调用的是jdk的基本实现,如果需要代理的目标对象没有实现任何接口,那么是无法为他创建代理对象的,这也是致命的缺陷。而在Spring中我们可以不编写上述如此复杂的代码,只需要利用AOP,就能够轻轻松松实现上述功能,当然,Spring AOP的底层实现也依赖的是动态代理。

AOP的核心概念及术语
  • 切面(Aspect): 指关注点模块化,这个关注点可能会横切多个对象。事务管理是企业级Java应用中有关横切关注点的例子。 在Spring AOP中,切面可以使用通用类基于模式的方式(schema-based approach)或者在普通类中以@Aspect注解(@AspectJ 注解方式)来实现。
  • 连接点(Join point): 在程序执行过程中某个特定的点,例如某个方法调用的时间点或者处理异常的时间点。在Spring AOP中,一个连接点总是代表一个方法的执行。
  • 通知(Advice): 在切面的某个特定的连接点上执行的动作。通知有多种类型,包括“around”, “before” and “after”等等。通知的类型将在后面的章节进行讨论。 许多AOP框架,包括Spring在内,都是以拦截器做通知模型的,并维护着一个以连接点为中心的拦截器链。
  • 切点(Pointcut): 匹配连接点的断言。通知和切点表达式相关联,并在满足这个切点的连接点上运行(例如,当执行某个特定名称的方法时)。切点表达式如何和连接点匹配是AOP的核心:Spring默认使用AspectJ切点语义。
  • 引入(Introduction): 声明额外的方法或者某个类型的字段。Spring允许引入新的接口(以及一个对应的实现)到任何被通知的对象上。例如,可以使用引入来使bean实现 IsModified接口, 以便简化缓存机制(在AspectJ社区,引入也被称为内部类型声明(inter))。
  • 目标对象(Target object): 被一个或者多个切面所通知的对象。也被称作被通知(advised)对象。既然Spring AOP是通过运行时代理实现的,那么这个对象永远是一个被代理(proxied)的对象。
  • AOP代理(AOP proxy):AOP框架创建的对象,用来实现切面契约(aspect contract)(包括通知方法执行等功能)。在Spring中,AOP代理可以是JDK动态代理或CGLIB代理。
  • 织入(Weaving): 把切面连接到其它的应用程序类型或者对象上,并创建一个被被通知的对象的过程。这个过程可以在编译时(例如使用AspectJ编译器)、类加载时或运行时中完成。 Spring和其他纯Java AOP框架一样,是在运行时完成织入的。
AOP的通知类型
  • 前置通知(Before advice): 在连接点之前运行但无法阻止执行流程进入连接点的通知(除非它引发异常)。
  • 后置返回通知(After returning advice):在连接点正常完成后执行的通知(例如,当方法没有抛出任何异常并正常返回时)。
  • 后置异常通知(After throwing advice): 在方法抛出异常退出时执行的通知。
  • 后置通知(总会执行)(After (finally) advice): 当连接点退出的时候执行的通知(无论是正常返回还是异常退出)。
  • 环绕通知(Around Advice):环绕连接点的通知,例如方法调用。这是最强大的一种通知类型,。环绕通知可以在方法调用前后完成自定义的行为。它可以选择是否继续执行连接点或直接返回自定义的返回值又或抛出异常将执行结束。
AOP的应用场景
  • 日志管理
  • 权限认证
  • 安全检查
  • 事务控制

2、Spring AOP的简单配置

​ 在上述代码中我们是通过动态代理的方式实现日志功能的,但是比较麻烦,现在我们将要使用spring aop的功能实现此需求,其实通俗点说的话,就是把LogUtil的工具类换成另外一种实现方式。

1、添加pom依赖
        
        <dependency>
            <groupId>cglibgroupId>
            <artifactId>cglibartifactId>
            <version>3.3.0version>
        dependency>
        
        <dependency>
            <groupId>org.aspectjgroupId>
            <artifactId>aspectjweaverartifactId>
            <version>1.9.5version>
        dependency>
        
        <dependency>
            <groupId>aopalliancegroupId>
            <artifactId>aopallianceartifactId>
            <version>1.0version>
        dependency>
        
        <dependency>
            <groupId>org.springframeworkgroupId>
            <artifactId>spring-aspectsartifactId>
            <version>5.2.3.RELEASEversion>
        dependency>
2、编写配置
  • 将目标类和切面类加入到IOC容器中,在对应的类上添加组件注解

    • 给LogUtil添加@Component注解

    • 给MyCalculator添加@Service注解

    • 添加自动扫描的配置

      
      <context:component-scan base-package="com.mashibing">context:component-scan>
      
  • 设置程序中的切面类

    • 在LogUtil.java中添加@Aspect注解
  • 设置切面类中的方法是什么时候在哪里执行

    package com.mashibing.util;
    
    import org.aspectj.lang.annotation.*;
    import org.springframework.stereotype.Component;
    
    import java.lang.reflect.Method;
    import java.util.Arrays;
    
    @Component
    @Aspect
    public class LogUtil {
    
        /*
        设置下面方法在什么时候运行
            @Before:在目标方法之前运行:前置通知
            @After:在目标方法之后运行:后置通知
            @AfterReturning:在目标方法正常返回之后:返回通知
            @AfterThrowing:在目标方法抛出异常后开始运行:异常通知
            @Around:环绕:环绕通知
    
            当编写完注解之后还需要设置在哪些方法上执行,使用表达式
            execution(访问修饰符  返回值类型 方法全称)
         */
        @Before("execution( public int com.mashibing.inter.MyCalculator.*(int,int))")
        public static void start(){
    //        System.out.println("XXX方法开始执行,使用的参数是:"+ Arrays.asList(objects));
    //        System.out.println(method.getName()+"方法开始执行,参数是:"+ Arrays.asList(objects));
            System.out.println("方法开始执行,参数是:");
        }
    
        @AfterReturning("execution( public int com.mashibing.inter.MyCalculator.*(int,int))")
        public static void stop(){
    //        System.out.println("XXX方法执行结束,结果是:"+ Arrays.asList(objects));
    //        System.out.println(method.getName()+"方法执行结束,结果是:"+ Arrays.asList(objects));
            System.out.println("方法执行完成,结果是:");
    
        }
    
        @AfterThrowing("execution( public int com.mashibing.inter.MyCalculator.*(int,int))")
        public static void logException(){
    //        System.out.println(method.getName()+"方法出现异常:"+ e.getMessage());
            System.out.println("方法出现异常:");
        }
    
        @After("execution( public int com.mashibing.inter.MyCalculator.*(int,int))")
        public static void end(){
    //        System.out.println(method.getName()+"方法执行结束了......");
            System.out.println("方法执行结束了......");
        }
    }
    
  • 开启基于注解的aop的功能

    
    <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"
           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/context
           http://www.springframework.org/schema/context/spring-context.xsd
           http://www.springframework.org/schema/aop
           https://www.springframework.org/schema/aop/spring-aop.xsd
    ">
        <context:component-scan base-package="com.mashibing">context:component-scan>
        <aop:aspectj-autoproxy>aop:aspectj-autoproxy>
    beans>
    
3、测试

MyTest.java

import com.mashibing.inter.Calculator;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
    public static void main(String[] args){
        ApplicationContext context = new ClassPathXmlApplicationContext("aop.xml");
        Calculator bean = context.getBean(Calculator.class);
        bean.add(1,1);
    }
}

​ spring AOP的动态代理方式是jdk自带的方式,容器中保存的组件是代理对象com.sun.proxy.$Proxy对象

4、通过cglib来创建代理对象

MyCalculator.java

package com.mashibing.inter;

import org.springframework.stereotype.Service;

@Service
public class MyCalculator {
    public int add(int i, int j) {
        int result = i + j;
        return result;
    }

    public int sub(int i, int j) {
        int result = i - j;
        return result;
    }

    public int mult(int i, int j) {
        int result = i * j;
        return result;
    }

    public int div(int i, int j) {
        int result = i / j;
        return result;
    }
}

MyTest.java

public class MyTest {
    public static void main(String[] args){
        ApplicationContext context = new ClassPathXmlApplicationContext("aop.xml");
        MyCalculator bean = context.getBean(MyCalculator.class);
        bean.add(1,1);
        System.out.println(bean);
        System.out.println(bean.getClass());
    }
}

​ 可以通过cglib的方式来创建代理对象,此时不需要实现任何接口,代理对象是

class com.mashibing.inter.MyCalculator E n h a n c e r B y S p r i n g C G L I B EnhancerBySpringCGLIB EnhancerBySpringCGLIB1f93b605类型

综上所述:在spring容器中,如果有接口,那么会使用jdk自带的动态代理,如果没有接口,那么会使用cglib的动态代理。动态代理的实现原理,后续会详细讲。

注意:
1、切入点表达式

​ 在使用表达式的时候,除了之前的写法之外,还可以使用通配符的方式:

​ *:

​ 1、匹配一个或者多个字符

​ execution( public int com.mashibing.inter.My*alculator.*(int,int))

​ 2、匹配任意一个参数,

​ execution( public int com.mashibing.inter.MyCalculator.*(int,*))

​ 3、只能匹配一层路径,如果项目路径下有多层目录,那么*只能匹配一层路径

​ 4、权限位置不能使用*,如果想表示全部权限,那么不写即可

​ execution( * com.mashibing.inter.MyCalculator.*(int,*))

​ …:

​ 1、匹配多个参数,任意类型参数

​ execution( * com.mashibing.inter.MyCalculator.*(…))

​ 2、匹配任意多层路径

​ execution( * com.mashibing…MyCalculator.*(…))

​ 在写表达式的时候,可以有N多种写法,但是有一种最偷懒和最精确的方式:

​ 最偷懒的方式:execution(* *(…)) 或者 execution(* *.*(…))

​ 最精确的方式:execution( public int com.mashibing.inter.MyCalculator.add(int,int))

​ 除此之外,在表达式中还支持 &&、||、!的方式

​ &&:两个表达式同时

​ execution( public int com.mashibing.inter.MyCalculator.*(…)) && execution(* *.*(int,int) )

​ ||:任意满足一个表达式即可

​ execution( public int com.mashibing.inter.MyCalculator.*(…)) && execution(* *.*(int,int) )

​ !:只要不是这个位置都可以进行切入

​ &&:两个表达式同时

​ execution( public int com.mashibing.inter.MyCalculator.*(…))

2、通知方法的执行顺序

​ 在之前的代码中大家一直对通知的执行顺序有疑问,其实执行的结果并没有错,大家需要注意:

​ 1、正常执行:@Before—>@After—>@AfterReturning

​ 2、异常执行:@Before—>@After—>@AfterThrowing

3、获取方法的详细信息

​ 在上面的案例中,我们并没有获取Method的详细信息,例如方法名、参数列表等信息,想要获取的话其实非常简单,只需要添加JoinPoint参数即可。

LogUtil.java

package com.mashibing.util;

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

import java.util.Arrays;

@Component
@Aspect
public class LogUtil {

    @Before("execution( public int com.mashibing.inter.MyCalculator.*(int,int))")
    public static void start(JoinPoint joinPoint){
        Object[] args = joinPoint.getArgs();
        String name = joinPoint.getSignature().getName();
        System.out.println(name+"方法开始执行,参数是:"+ Arrays.asList(args));
    }

    @AfterReturning("execution( public int com.mashibing.inter.MyCalculator.*(int,int))")
    public static void stop(JoinPoint joinPoint){
        String name = joinPoint.getSignature().getName();
        System.out.println(name+"方法执行完成,结果是:");

    }

    @AfterThrowing("execution( public int com.mashibing.inter.MyCalculator.*(int,int))")
    public static void logException(JoinPoint joinPoint){
        String name = joinPoint.getSignature().getName();
        System.out.println(name+"方法出现异常:");
    }

    @After("execution( public int com.mashibing.inter.MyCalculator.*(int,int))")
    public static void end(JoinPoint joinPoint){
        String name = joinPoint.getSignature().getName();
        System.out.println(name+"方法执行结束了......");
    }
}

​ 刚刚只是获取了方法的信息,但是如果需要获取结果,还需要添加另外一个方法参数,并且告诉spring使用哪个参数来进行结果接收

LogUtil.java

    @AfterReturning(value = "execution( public int com.mashibing.inter.MyCalculator.*(int,int))",returning = "result")
    public static void stop(JoinPoint joinPoint,Object result){
        String name = joinPoint.getSignature().getName();
        System.out.println(name+"方法执行完成,结果是:"+result);

    }

​ 也可以通过相同的方式来获取异常的信息

LogUtil.java

    @AfterThrowing(value = "execution( public int com.mashibing.inter.MyCalculator.*(int,int))",throwing = "exception")
    public static void logException(JoinPoint joinPoint,Exception exception){
        String name = joinPoint.getSignature().getName();
        System.out.println(name+"方法出现异常:"+exception);
    }
4、spring对通过方法的要求

​ spring对于通知方法的要求并不是很高,你可以任意改变方法的返回值和方法的访问修饰符,但是唯一不能修改的就是方法的参数,会出现参数绑定的错误,原因在于通知方法是spring利用反射调用的,每次方法调用得确定这个方法的参数的值。

LogUtil.java

    @After("execution( public int com.mashibing.inter.MyCalculator.*(int,int))")
    private int end(JoinPoint joinPoint,String aa){
//        System.out.println(method.getName()+"方法执行结束了......");
        String name = joinPoint.getSignature().getName();
        System.out.println(name+"方法执行结束了......");
        return 0;
    }
5、表达式的抽取

如果在实际使用过程中,多个方法的表达式是一致的话,那么可以考虑将切入点表达式抽取出来:

​ a、随便生命一个没有实现的返回void的空方法

​ b、给方法上标注@Potintcut注解

package com.mashibing.util;

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

import java.util.Arrays;

@Component
@Aspect
public class LogUtil {
    
    @Pointcut("execution( public int com.mashibing.inter.MyCalculator.*(int,int))")
    public void myPoint(){}
    
    @Before("myPoint()")
    public static void start(JoinPoint joinPoint){
        Object[] args = joinPoint.getArgs();
        String name = joinPoint.getSignature().getName();
        System.out.println(name+"方法开始执行,参数是:"+ Arrays.asList(args));
    }

    @AfterReturning(value = "myPoint()",returning = "result")
    public static void stop(JoinPoint joinPoint,Object result){
        String name = joinPoint.getSignature().getName();
        System.out.println(name+"方法执行完成,结果是:"+result);

    }

    @AfterThrowing(value = "myPoint()",throwing = "exception")
    public static void logException(JoinPoint joinPoint,Exception exception){
        String name = joinPoint.getSignature().getName();
        System.out.println(name+"方法出现异常:"+exception.getMessage());
    }

    @After("myPoint()")
    private int end(JoinPoint joinPoint){
        String name = joinPoint.getSignature().getName();
        System.out.println(name+"方法执行结束了......");
        return 0;
    }
}
6、环绕通知的使用

LogUtil.java

package com.mashibing.util;

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

import java.util.Arrays;

@Component
@Aspect
public class LogUtil {
    @Pointcut("execution( public int com.mashibing.inter.MyCalculator.*(int,int))")
    public void myPoint(){}
    
    /**
     * 环绕通知是spring中功能最强大的通知
     * @param proceedingJoinPoint
     * @return
     */
    @Around("myPoint()")
    public Object myAround(ProceedingJoinPoint proceedingJoinPoint){
        Object[] args = proceedingJoinPoint.getArgs();
        String name = proceedingJoinPoint.getSignature().getName();
        Object proceed = null;
        try {
            System.out.println("环绕前置通知:"+name+"方法开始,参数是"+Arrays.asList(args));
            //利用反射调用目标方法,就是method.invoke()
            proceed = proceedingJoinPoint.proceed(args);
            System.out.println("环绕返回通知:"+name+"方法返回,返回值是"+proceed);
        } catch (Throwable e) {
            System.out.println("环绕异常通知"+name+"方法出现异常,异常信息是:"+e);
        }finally {
            System.out.println("环绕后置通知"+name+"方法结束");
        }
        return proceed;
    }
}

​ 总结:环绕通知的执行顺序是优于普通通知的,具体的执行顺序如下:

环绕前置–>普通前置–>目标方法执行–>环绕正常结束/出现异常–>环绕后置–>普通后置–>普通返回或者异常。

但是需要注意的是,如果出现了异常,那么环绕通知会处理或者捕获异常,普通异常通知是接收不到的,因此最好的方式是在环绕异常通知中向外抛出异常。

7、多切面运行的顺序

​ 如果有多个切面要进行执行,那么顺序是什么样的呢?

LogUtil.java

package com.mashibing.util;

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

import java.util.Arrays;

@Component
@Aspect
public class LogUtil {
    @Pointcut("execution( public int com.mashibing.inter.MyCalculator.*(int,int))")
    public void myPoint(){}
    @Before("myPoint()")
    public static void start(JoinPoint joinPoint){
//        System.out.println("XXX方法开始执行,使用的参数是:"+ Arrays.asList(objects));
//        System.out.println(method.getName()+"方法开始执行,参数是:"+ Arrays.asList(objects));
        Object[] args = joinPoint.getArgs();
        String name = joinPoint.getSignature().getName();
        System.out.println("Log:"+name+"方法开始执行,参数是:"+ Arrays.asList(args));
    }

    @AfterReturning(value = "myPoint()",returning = "result")
    public static void stop(JoinPoint joinPoint,Object result){
//        System.out.println("XXX方法执行结束,结果是:"+ Arrays.asList(objects));
//        System.out.println(method.getName()+"方法执行结束,结果是:"+ Arrays.asList(objects));
        String name = joinPoint.getSignature().getName();
        System.out.println("Log:"+name+"方法执行完成,结果是:"+result);

    }

    @AfterThrowing(value = "myPoint()",throwing = "exception")
    public static void logException(JoinPoint joinPoint,Exception exception){
//        System.out.println(method.getName()+"方法出现异常:"+ e.getMessage());
        String name = joinPoint.getSignature().getName();
        System.out.println("Log:"+name+"方法出现异常:"+exception.getMessage());
    }

    @After("myPoint()")
    private int end(JoinPoint joinPoint){
//        System.out.println(method.getName()+"方法执行结束了......");
        String name = joinPoint.getSignature().getName();
        System.out.println("Log:"+name+"方法执行结束了......");
        return 0;
    }

    /**
     * 环绕通知是spring中功能最强大的通知
     * @param proceedingJoinPoint
     * @return
     */
    //@Around("myPoint()")
    public Object myAround(ProceedingJoinPoint proceedingJoinPoint){
        Object[] args = proceedingJoinPoint.getArgs();
        String name = proceedingJoinPoint.getSignature().getName();
        Object proceed = null;
        try {
            System.out.println("环绕前置通知:"+name+"方法开始,参数是"+Arrays.asList(args));
            //利用反射调用目标方法,就是method.invoke()
            proceed = proceedingJoinPoint.proceed(args);
            System.out.println("环绕返回通知:"+name+"方法返回,返回值是"+proceed);
        } catch (Throwable e) {
            System.out.println("环绕异常通知"+name+"方法出现异常,异常信息是:"+e);
        }finally {
            System.out.println("环绕后置通知"+name+"方法结束");
        }
        return proceed;
    }
}

SecurityAspect.java

package com.mashibing.util;

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

import java.util.Arrays;

@Component
@Aspect
public class SecurityAspect {

    @Before("com.mashibing.util.LogUtil.myPoint()")
    public static void start(JoinPoint joinPoint){
        Object[] args = joinPoint.getArgs();
        String name = joinPoint.getSignature().getName();
        System.out.println("Security:"+name+"方法开始执行,参数是:"+ Arrays.asList(args));
    }

    @AfterReturning(value = "com.mashibing.util.LogUtil.myPoint()",returning = "result")
    public static void stop(JoinPoint joinPoint,Object result){
        String name = joinPoint.getSignature().getName();
        System.out.println("Security:"+name+"方法执行完成,结果是:"+result);

    }

    @AfterThrowing(value = "com.mashibing.util.LogUtil.myPoint()",throwing = "exception")
    public static void logException(JoinPoint joinPoint,Exception exception){
        String name = joinPoint.getSignature().getName();
        System.out.println("Security:"+name+"方法出现异常:"+exception.getMessage());
    }

    @After("com.mashibing.util.LogUtil.myPoint()")
    private int end(JoinPoint joinPoint){
        String name = joinPoint.getSignature().getName();
        System.out.println("Security:"+name+"方法执行结束了......");
        return 0;
    }

    /**
     * 环绕通知是spring中功能最强大的通知
     * @param proceedingJoinPoint
     * @return
     */
    //@Around("myPoint()")
    public Object myAround(ProceedingJoinPoint proceedingJoinPoint){
        Object[] args = proceedingJoinPoint.getArgs();
        String name = proceedingJoinPoint.getSignature().getName();
        Object proceed = null;
        try {
            System.out.println("环绕前置通知:"+name+"方法开始,参数是"+Arrays.asList(args));
            //利用反射调用目标方法,就是method.invoke()
            proceed = proceedingJoinPoint.proceed(args);
            System.out.println("环绕返回通知:"+name+"方法返回,返回值是"+proceed);
        } catch (Throwable e) {
            System.out.println("环绕异常通知"+name+"方法出现异常,异常信息是:"+e);
        }finally {
            System.out.println("环绕后置通知"+name+"方法结束");
        }
        return proceed;
    }
}

​ 在spring中,默认是按照切面名称的字典顺序进行执行的,但是如果想自己改变具体的执行顺序的话,可以使用@Order注解来解决,数值越小,优先级越高。

LogUtil.java

package com.mashibing.util;

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

import java.util.Arrays;

@Component
@Aspect
@Order(2)
public class LogUtil {
    @Pointcut("execution( public int com.mashibing.inter.MyCalculator.*(int,int))")
    public void myPoint(){}
    @Before("myPoint()")
    public static void start(JoinPoint joinPoint){
//        System.out.println("XXX方法开始执行,使用的参数是:"+ Arrays.asList(objects));
//        System.out.println(method.getName()+"方法开始执行,参数是:"+ Arrays.asList(objects));
        Object[] args = joinPoint.getArgs();
        String name = joinPoint.getSignature().getName();
        System.out.println("Log:"+name+"方法开始执行,参数是:"+ Arrays.asList(args));
    }

    @AfterReturning(value = "myPoint()",returning = "result")
    public static void stop(JoinPoint joinPoint,Object result){
//        System.out.println("XXX方法执行结束,结果是:"+ Arrays.asList(objects));
//        System.out.println(method.getName()+"方法执行结束,结果是:"+ Arrays.asList(objects));
        String name = joinPoint.getSignature().getName();
        System.out.println("Log:"+name+"方法执行完成,结果是:"+result);

    }

    @AfterThrowing(value = "myPoint()",throwing = "exception")
    public static void logException(JoinPoint joinPoint,Exception exception){
//        System.out.println(method.getName()+"方法出现异常:"+ e.getMessage());
        String name = joinPoint.getSignature().getName();
        System.out.println("Log:"+name+"方法出现异常:"+exception.getMessage());
    }

    @After("myPoint()")
    private int end(JoinPoint joinPoint){
//        System.out.println(method.getName()+"方法执行结束了......");
        String name = joinPoint.getSignature().getName();
        System.out.println("Log:"+name+"方法执行结束了......");
        return 0;
    }

    /**
     * 环绕通知是spring中功能最强大的通知
     * @param proceedingJoinPoint
     * @return
     */
    //@Around("myPoint()")
    public Object myAround(ProceedingJoinPoint proceedingJoinPoint){
        Object[] args = proceedingJoinPoint.getArgs();
        String name = proceedingJoinPoint.getSignature().getName();
        Object proceed = null;
        try {
            System.out.println("环绕前置通知:"+name+"方法开始,参数是"+Arrays.asList(args));
            //利用反射调用目标方法,就是method.invoke()
            proceed = proceedingJoinPoint.proceed(args);
            System.out.println("环绕返回通知:"+name+"方法返回,返回值是"+proceed);
        } catch (Throwable e) {
            System.out.println("环绕异常通知"+name+"方法出现异常,异常信息是:"+e);
        }finally {
            System.out.println("环绕后置通知"+name+"方法结束");
        }
        return proceed;
    }
}

SecurityAspect.java

package com.mashibing.util;

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

import java.util.Arrays;

@Component
@Aspect
@Order(1)
public class SecurityAspect {

    @Before("com.mashibing.util.LogUtil.myPoint()")
    public static void start(JoinPoint joinPoint){
        Object[] args = joinPoint.getArgs();
        String name = joinPoint.getSignature().getName();
        System.out.println("Security:"+name+"方法开始执行,参数是:"+ Arrays.asList(args));
    }

    @AfterReturning(value = "com.mashibing.util.LogUtil.myPoint()",returning = "result")
    public static void stop(JoinPoint joinPoint,Object result){
        String name = joinPoint.getSignature().getName();
        System.out.println("Security:"+name+"方法执行完成,结果是:"+result);

    }

    @AfterThrowing(value = "com.mashibing.util.LogUtil.myPoint()",throwing = "exception")
    public static void logException(JoinPoint joinPoint,Exception exception){
        String name = joinPoint.getSignature().getName();
        System.out.println("Security:"+name+"方法出现异常:"+exception.getMessage());
    }

    @After("com.mashibing.util.LogUtil.myPoint()")
    private int end(JoinPoint joinPoint){
        String name = joinPoint.getSignature().getName();
        System.out.println("Security:"+name+"方法执行结束了......");
        return 0;
    }

    /**
     * 环绕通知是spring中功能最强大的通知
     * @param proceedingJoinPoint
     * @return
     */
    //@Around("myPoint()")
    public Object myAround(ProceedingJoinPoint proceedingJoinPoint){
        Object[] args = proceedingJoinPoint.getArgs();
        String name = proceedingJoinPoint.getSignature().getName();
        Object proceed = null;
        try {
            System.out.println("环绕前置通知:"+name+"方法开始,参数是"+Arrays.asList(args));
            //利用反射调用目标方法,就是method.invoke()
            proceed = proceedingJoinPoint.proceed(args);
            System.out.println("环绕返回通知:"+name+"方法返回,返回值是"+proceed);
        } catch (Throwable e) {
            System.out.println("环绕异常通知"+name+"方法出现异常,异常信息是:"+e);
        }finally {
            System.out.println("环绕后置通知"+name+"方法结束");
        }
        return proceed;
    }
}

​ 如果需要添加环绕通知呢,具体的执行顺序又会是什么顺序呢?

​ 因为环绕通知在进行添加的时候,是在切面层引入的,所以在哪个切面添加环绕通知,那么就会在哪个切面执行。

3、基于配置的AOP配置

​ 之前我们讲解了基于注解的AOP配置方式,下面我们开始讲一下基于xml的配置方式,虽然在现在的企业级开发中使用注解的方式比较多,但是你不能不会,因此需要简单的进行配置,注解配置快速简单,配置的方式共呢个完善。

1、将所有的注解都进行删除

2、添加配置文件

aop.xml


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

    <context:component-scan base-package="com.mashibing">context:component-scan>
    <aop:aspectj-autoproxy>aop:aspectj-autoproxy>

    <bean id="logUtil" class="com.mashibing.util.LogUtil2">bean>
    <bean id="securityAspect" class="com.mashibing.util.SecurityAspect">bean>
    <bean id="myCalculator" class="com.mashibing.inter.MyCalculator">bean>
    <aop:config>
        <aop:pointcut id="globalPoint" expression="execution(public int com.mashibing.inter.MyCalculator.*(int,int))"/>
        <aop:aspect ref="logUtil">
            <aop:pointcut id="mypoint" expression="execution(public int com.mashibing.inter.MyCalculator.*(int,int))"/>
            <aop:before method="start" pointcut-ref="mypoint">aop:before>
            <aop:after method="end" pointcut-ref="mypoint">aop:after>
            <aop:after-returning method="stop" pointcut-ref="mypoint" returning="result">aop:after-returning>
            <aop:after-throwing method="logException" pointcut-ref="mypoint" throwing="exception">aop:after-throwing>
            <aop:around method="myAround" pointcut-ref="mypoint">aop:around>
        aop:aspect>
        <aop:aspect ref="securityAspect">
            <aop:before method="start" pointcut-ref="globalPoint">aop:before>
            <aop:after method="end" pointcut-ref="globalPoint">aop:after>
            <aop:after-returning method="stop" pointcut-ref="globalPoint" returning="result">aop:after-returning>
            <aop:after-throwing method="logException" pointcut-ref="globalPoint" throwing="exception">aop:after-throwing>
            <aop:around method="myAround" pointcut-ref="mypoint">aop:around>
        aop:aspect>
    aop:config>
beans>

maven的介绍与使用

1、maven的简单介绍

​ Maven是Apache下的项目管理工具,它由纯Java语言开发,可以帮助我们更方便的管理和构建Java项目。

​ maven的优点

​ 1、 jar包管理:

​ a) 从Maven中央仓库获取标准的规范的jar包以及相关依赖的jar包,避免自己下载到错误的jar包;

​ b) 本地仓库统一管理jar包,使jar包与项目分离,减轻项目体积。

​ 2、 maven是跨平台的可以在window、linux上使用。

​ 3、 清晰的项目结构;

​ 4、 多工程开发,将模块拆分成若干工程,利于团队协作开发。

​ 5、 一键构建项目:使用命令可以对项目进行一键构建。

2、maven的安装

​ maven官网:https://maven.apache.org/

​ maven仓库:https://mvnrepository.com/

​ 安装步骤:

1、安装jdk
2、从官网中下载对应的版本
3、解压安装,然后配置环境变量,需要配置MAVEN_HOME,并且将bin目录添加到path路径下
4、在命令行中输入mvn -v,看到版本信息表示安装成功

3、maven的基本常识

maven如何获取jar包

​ maven通过坐标的方式来获取 jar包,坐标组成为:公司/组织(groupId)+项目名(artifactId)+版本(version)组成,可以从互联网,本地等多种仓库源获取jar包

maven仓库的分类

​ 本地仓库:本地仓库就是开发者本地已经下载下来的或者自己打包所有jar包的依赖仓库,本地仓库路径配置在maven对应的conf/settings.xml配置文件。

​ 私有仓库:私有仓库可以理解为自己公司的仓库,也叫Nexus私服

​ 中央仓库:中央仓库即maven默认下载的仓库地址,是maven维护的

maven的常用仓库

​ 由于网络访问的原因,在国内如果需要下载国外jar包的时候会受限,因此一般在使用过程中需要修改maven的配置文件,将下载jar包的仓库地址修改为国内的源,常用的是阿里云的mvn仓库,修改配置如下:

<mirror>
<id>alimavenid>
<name>aliyun mavenname>
<url>http://maven.aliyun.com/nexus/content/groups/public/url>
<mirrorOf>centralmirrorOf>
mirror>

4、maven常用命令

  • clean:清理编译后的目录
  • compile:编译,只编译main目录,不编译test中的代码
  • test-compile:编译test目录下的代码
  • test:运行test中的代码
  • package:打包,将项目打包成jar包或者war包
  • install:发布项目到本地仓库,用在打jar包上,打成的jar包可以被其他项目使用
  • deploy:打包后将其安装到pom文件中配置的远程仓库
  • site:生成站点目录

你可能感兴趣的:(spring,spring,java,后端)