Java进阶:Spring

1997年,IBM提出了EJB的思想。

1998年,SUN指定开发标准规范EJB1.0.

1999年,EJB1.1发布。

2001年,EJB2.0发布。

2003年,EJB2.1发布。

2006年,EJB3.0发布。

在这里插入图片描述

Rod Johnson ( Spring 之父)

Expert One-to-One J2EE Design and Development(2002)阐述了J2EE使用EJB开发设计的优点和解决方案。

Expert One-to-One J2EE Development without EJB(2004)阐述了J2EE开发部使用EJB的解决方式(Spring雏形)。

2017 年9 月份发布了 Spring 的最新版本 Spring5.0通用版(GA)

1.3 Spring的优势(理解)

  • 方便解耦,简化开发

通过Spring提供的IoC容器,可以将对象间的依赖关系交由Spring进行控制,避免硬编码所造成的过度耦合。

用户也不必再为单例模式类、属性文件解析等这些很底层的需求编写代码,可以更专注于上层的应用。

  • AOP 编程的支持

通过Spring的AOP功能,方便进行面向切面变成,许多不容易用传统OOP实现的功能可以通过AOP轻松实现。

AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,

OOP为Object Oriented Programming的缩写,意为:面向对象编程。。

  • 声明式事务的支持

可以将我们从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活的进行事务管理,提高开发效率和质量。

  • 方便程序的测试

可以用非容器依赖的编程方式进行几乎所有的测试工作,测试不再是昂贵的操作,而是随手可做的事情。

  • 方便集成各种优秀框架

Spring对各种优秀框架(Struts、Hibemate、Hessian、Quartz等)的支持。

  • 降低JavaEE API的使用难度

Spring 对 JavaEE API(如JDBC、JavaMail、远程调用等)进行了薄薄的封装层,使得这些API的使用难度大为降低。

  • Java源码是经典学习带典范

Spring的源代码设计精妙、结构清晰、匠心独拥,处处体现着大师对Java设计模式灵活运用以及对Java技术的高深造诣。它的源代码无疑是Java技术的最佳实践范例。

1.4 Spring的体系结构(了解)

在这里插入图片描述

2. spring快速入门


2.1 Spring程序开发步骤

  1. 导入 Spring 开发的基本包坐标在这里插入图片描述

  2. 编写 Dao 接口和实现类在这里插入图片描述在这里插入图片描述

  3. 创建 Spring 核心配置文件

在这里插入图片描述

  1. 在 Spring 配置文件中配置 UserDaoImpl

新建applicationContext.xml在这里插入图片描述

配置UserDaoImpl在这里插入图片描述

  1. 使用 Spring 的 API 获得 Bean 实例

新建UserDaoDemo

在这里插入图片描述在这里插入图片描述在这里插入图片描述

2.1.1 导入Spring开发的基本包坐标

<spring.version>5.0.5.RELEASE</spring.version>

org.springframework

spring-context

${spring.version}

2.1.2 编写Dao接口和实现类

public interface UserDao {

public void save();

}

public class UserDaoImpl implements UserDao {

@Override

public void save() {

System.out.println(“UserDao save method running…”);

}

}

2.1.3 创建Spring核心配置文件

在类路径下(resources)创建applicationContext.xml配置文件

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

<beans xmlns=“http://www.springframework.org/schema/beans” xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance”

xsi:schemaLocation="

http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

2.1.4在Spring配置文件中配置UserDaoImpl
<?xml version="1.0" encoding="UTF-8" ?>

<beans xmlns=“http://www.springframework.org/schema/beans” xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance”

xsi:schemaLocation="

http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

2.1.5 使用Spring的API获得Bean实例

@Test

public void test1(){

ApplicationContext applicationContext = new

ClassPathXmlApplicationContext(“applicationContext.xml”);

UserDao userDao = (UserDao) applicationContext.getBean(“userDao”); userDao.save();

}

3. Spring配置文件


3.1 Bean标签基本配置

用于配置对象交由Spring 来创建。

默认情况下它调用的是类中的无参构造函数,如果没有无参构造函数则不能创建成功。

基本属性:

id:Bean实例在Spring容器中的唯一标识

class:Bean的全限定名称

3.2 Bean标签范围配置

scope:指对象的作用范围,取值如下:

| 取值范围 | 说明 |

| — | — |

| singleton | 默认值,单例的 |

| prototype | 多例的 |

| request | WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 request 域中 |

| session | WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 session 域中 |

| global session | WEB 项目中,应用在 Portlet 环境,如果没有 Portlet 环境那么globalSession 相当于 session |

  1. 当scope的取值为singleton时

​ Bean的实例化个数:1个

​ Bean的实例化时机:当Spring核心文件被加载时,实例化配置的Bean实例

​ Bean的生命周期:

  • 对象创建:当应用加载,创建容器时,对象就被创建了

  • 对象运行:只要容器在,对象一直活着

  • 对象销毁:当应用卸载,销毁容器时,对象就被销毁了

在这里插入图片描述

  1. 当scope的取值为prototype时

​ Bean的实例化个数:多个

​ Bean的实例化时机:当调用getBean()方法时实例化Bean

  • 对象创建:当使用对象时,创建新的对象实例

  • 对象运行:只要对象在使用中,就一直活着

  • 对象销毁:当对象长时间不用时,被 Java 的垃圾回收器回收了

在这里插入图片描述

3.3 Bean生命周期配置

  • init-method:指定类中的初始化方法名称

  • destroy-method:指定类中销毁方法名称

在这里插入图片描述

3.4 Bean实例化三种方式

3.4.1使用无参构造方法实例化

它会根据默认无参构造方法来创建类对象,如果bean中没有默认无参构造函数,将会创建失败

3.4.1工厂静态方法实例化

​ 工厂的静态方法返回Bean实例

public class StaticFactoryBean {

public static UserDao createUserDao(){

return new UserDaoImpl();

}

}

<bean id=“userDao” class=“com.itheima.factory.StaticFactoryBean”

factory-method=“createUserDao” />

3.4.3工厂实例方法实例化

​ 工厂的非静态方法返回Bean实例

public class DynamicFactoryBean {

public UserDao createUserDao(){

return new UserDaoImpl();

}

}

3.5 Bean的依赖注入入门

①创建 UserService,UserService 内部在调用 UserDao的save() 方法

public class UserServiceImpl implements UserService {

@Override

public void save() {

ApplicationContext applicationContext = new

ClassPathXmlApplicationContext(“applicationContext.xml”); UserDao userDao = (UserDao) applicationContext.getBean(“userDao”);

userDao.save();

}

}

②将 UserServiceImpl 的创建权交给 Spring

③从 Spring 容器中获得 UserService 进行操作

ApplicationContext applicationContext = new ClassPathXmlApplicationContext(“applicationContext.xml”);

UserService userService = (UserService) applicationContext.getBean(“userService”);

userService.save();

3.6 Bean的依赖注入概念

依赖注入(Dependency Injection):它是 Spring 框架核心 IC 的具体实现。

在编写程序时,通过控制反转,把对象的创建交给了 Spring,但是代码中不可能出现没有依赖的情况。

IOC 解耦只是降低他们的依赖关系,但不会消除。例如:业务层仍会调用持久层的方法。

那这种业务层和持久层的依赖关系,在使用 Spring 之后,就让 Spring 来维护了。

简单的说,就是坐等框架把持久层对象传入业务层,而不用我们自己去获取

3.7 Bean的依赖注入方式

①构造方法

​ 创建有参构造

public class UserServiceImpl implements UserService {

@Override

public void save() {

ApplicationContext applicationContext = new

ClassPathXmlApplicationContext(“applicationContext.xml”); UserDao userDao = (UserDao) applicationContext.getBean(“userDao”);

userDao.save();

}

}

​ 配置Spring容器调用有参构造时进行注入

②set方法

​ 在UserServiceImpl中添加setUserDao方法

public class UserServiceImpl implements UserService {

private UserDao userDao;

public void setUserDao(UserDao userDao) {

this.userDao = userDao;

}

@Override

public void save() {

userDao.save();

}

}

​ 配置Spring容器调用set方法进行注入

set方法:P命名空间注入

​ P命名空间注入本质也是set方法注入,但比起上述的set方法注入更加方便,主要体现在配置文件中,如下:

​ 首先,需要引入P命名空间:

xmlns:p=“http://www.springframework.org/schema/p”

其次,需要修改注入方式

<bean id=“userService” class=“com.itheima.service.impl.UserServiceImpl” p:userDao-

ref=“userDao”/>

在这里插入图片描述

3.8 Bean的依赖注入的数据类型

上面的操作,都是注入的引用Bean,处了对象的引用可以注入,普通数据类型,集合等都可以在容器中进行注入。

注入数据的三种数据类型

  • 普通数据类型 用value

  • 引用数据类型 用ref

  • 集合数据类型

其中引用数据类型,此处就不再赘述了,之前的操作都是对UserDao对象的引用进行注入的,下面将以set方法注入为例,演示普通数据类型和集合数据类型的注入。

Bean的依赖注入的数据类型

(1)普通数据类型的注入 用value

public class UserDaoImpl implements UserDao {

private String company;

private int age;

public void setCompany(String company) {

this.company = company;

}

public void setAge(int age) {

this.age = age;

}

public void save() {

System.out.println(company+“===”+age);

System.out.println(“UserDao save method running…”);

}

}

(2)集合数据类型(List)的注入

public class UserDaoImpl implements UserDao {

private List strList;

public void setStrList(List strList) {

this.strList = strList;

}

public void save() {

System.out.println(strList);

System.out.println(“UserDao save method running…”);

}

}

aaa

bbb

ccc

(3)集合数据类型(List)的注入

public class UserDaoImpl implements UserDao {

private List userList;

public void setUserList(List userList) {

this.userList = userList;

}

public void save() {

System.out.println(userList);

System.out.println(“UserDao save method running…”);

}

}

(4)集合数据类型( Map<String,User> )的注入

public class UserDaoImpl implements UserDao {

private Map<String,User> userMap;

public void setUserMap(Map<String, User> userMap) {

this.userMap = userMap;

}

public void save() {

System.out.println(userMap);

System.out.println(“UserDao save method running…”);

}

}

(5)集合数据类型(Properties)的注入

public class UserDaoImpl implements UserDao {

private Properties properties;

public void setProperties(Properties properties) {

this.properties = properties;

}

public void save() {

System.out.println(properties);

System.out.println(“UserDao save method running…”);

}

}

aaa

bbb

ccc

3.9 引入其他配置文件(分模块开发)

实际开发中,Spring的配置内容非常多,这就导致Spring配置很繁杂且体积很大,所以,可以将部分配置拆解到其他配置文件中,而在Spring主配置文件通过import标签进行加载

在这里插入图片描述

3.10 知识要点

标签

id属性:在容器中Bean实例的唯一标识,不允许重复。

class属性:要实例化的Bean的全限定名。

scope属性:bean的作用范围,常用Singleton(默认)和prototype

标签:属性注入

name属性:属性名称

value属性:注入的普通属性值

ref属性:注入的对象引用值

标签

标签

标签

标签

标签:导入其他的Spring的分文件

4. spring相关API


4.1 ApplicationContext的继承体系

applicationContext:接口类型,代表应用上下文,可以通过其实例获得 Spring 容器中的 Bean 对象

在这里插入图片描述

4.2 ApplicationContext的实现类

  1. ClassPathXmlApplicationContext :它是从类的根路径下加载配置文件 推荐使用这种

  2. FileSystemXmlApplicationContext :它是从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置。

  3. AnnotationConfigApplicationContext:当使用注解配置容器对象时,需要使用此类来创建 spring 容器。它用来读取注解。

4.3 getBean()方法使用

源码:

public Object getBean(String name) throws BeansException {

assertBeanFactoryActive();

return getBeanFactory().getBean(name);

}

public T getBean(Class requiredType) throws BeansException { assertBeanFactoryActive();

return getBeanFactory().getBean(requiredType);

}

其中,当参数的数据类型是字符串时,表示根据Bean的id从容器中获得Bean实例,返回是Object,需要强转。

当参数的数据类型是Class类型时,表示根据类型从容器中匹配Bean实例,当容器中相同类型的Bean有多个时,则此方法会报错

getBean()方法使用

ApplicationContext applicationContext = new

ClassPathXmlApplicationContext(“applicationContext.xml”);

UserService userService1 = (UserService) applicationContext.getBean(“userService”);

UserService userService2 = applicationContext.getBean(UserService.class);

二、SpringIOC和DI注解开发

=============================================================================

1.Spring配置数据源


1.1 数据源(连接池)的作用

数据源(连接池)是提高程序性能如出现的

事先实例化数据源,初始化部分连接资源

使用连接资源时从数据源中获取

使用完毕后将连接资源归还给数据源

常见的数据源(连接池):DBCP、C3P0、BoneCP、Druid等

开发步骤

①导入数据源的坐标和数据库驱动坐标

②创建数据源对象

③设置数据源的基本连接数据

④使用数据源获取连接资源和归还连接资源

1.2 数据源的手动创建

①导入c3p0和druid的坐标

c3p0

c3p0

0.9.1.2

com.alibaba

druid

1.1.10

①导入mysql数据库驱动坐标

mysql

mysql-connector-java

8.0.22

②创建C3P0连接池

@Test

// 测试手动创建 c3p0 数据源

public void test1() throws PropertyVetoException, SQLException {

ComboPooledDataSource dataSource = new ComboPooledDataSource();

dataSource.setDriverClass(“com.mysql.jdbc.Driver”);

dataSource.setJdbcUrl(“jdbc:mysql://localhost:3306/test?useUnicode=true & characterEncoding=UTF-8 & serverTimezone=Asia/Shanghai”);

dataSource.setUser(“root”);

dataSource.setPassword(“131415”);

Connection connection = dataSource.getConnection();

System.out.println(connection);

connection.close();

}

②创建Druid连接池

@Test

public void test2() throws Exception {

DruidDataSource dataSource = new DruidDataSource();

dataSource.setDriverClassName(“com.mysql.jdbc.Driver”);

dataSource.setUrl(“jdbc:mysql://localhost:3306/test?useUnicode=true & characterEncoding=UTF-8 & serverTimezone=Asia/Shanghai”);

dataSource.setUsername(“root”);

dataSource.setPassword(“131415”);

DruidPooledConnection connection = dataSource.getConnection();

System.out.println(connection);

connection.close();

}

③提取jdbc.properties配置文件:resources目录下新建jdbc.properties

在这里插入图片描述

jdbc.driver=com.mysql.jdbc.Driver

jdbc.url=“jdbc:mysql://localhost:3306/test?useUnicode=true & characterEncoding=UTF-8 & serverTimezone=Asia/Shanghai”

jdbc.username=root

jdbc.password=131415

在这里插入图片描述

④读取jdbc.properties配置文件创建连接池

public void test3() throws Exception {

// 1. 读取配置文件

ResourceBundle rb = ResourceBundle.getBundle(“jdbc”);

// 2. 赋值

String driver = rb.getString(“jdbc.driver”);

String url = rb.getString(“jdbc.url”);

String username = rb.getString(“jdbc.username”);

String password = rb.getString(“jdbc.password”);

// 3. 创建数据源对象,设置连接参数

ComboPooledDataSource dataSource = new ComboPooledDataSource();

dataSource.setDriverClass(driver);

dataSource.setJdbcUrl(url);

dataSource.setUser(username);

dataSource.setPassword(password);

Connection connection = dataSource.getConnection();

System.out.println(connection);

connection.close();

}

1.3 Spring配置数据源

可以将DataSource的创建权交由Spring容器去完成

DataSource有无参构造方法,而Spring默认就是通过无参构造方法实例化对象的

DataSource要想使用需要通过set方法设置数据库连接信息,而Spring可以通过set方法进行字符串注入

<property name=“jdbcUrl”

value=“jdbc:mysql://localhost:3306/test?useUnicode=true & characterEncoding=UTF-8 & serverTimezone=Asia/Shanghai”/>

测试从容器当中获取数据源

@Test

// 测试Spring容器产生数据源对象

public void test4() throws Exception {

ApplicationContext app = new ClassPathXmlApplicationContext(“applicationContext.xml”);

DataSource dataSource = app.getBean(DataSource.class);

Connection connection = dataSource.getConnection();

System.out.println(connection);

connection.close();

}

1.4 抽取jdbc配置文件

applicationContext.xml加载jdbc.properties配置文件获得连接信息。

首先,需要引入context命名空间和约束路径:

  • 命名空间:xmlns:context=“http://www.springframework.org/schema/context”

  • 约束路径:http://www.springframework.org/schema/context

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

<context:property-placeholder location=“classpath:jdbc.properties”/>

在这里插入图片描述

1.5 知识要点

Spring容器加载properties文件

<context:property-placeholder location=“classpath:xx.properties”/>

2. Spring注解开发


2.1 Spring原始注解

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

Spring原始注解主要是替代的配置

| 注解 | 说明 |

| — | — |

| @Component | 使用在类上用于实例化Bean |

| @Controller | 使用在web层类上用于实例化Bean |

| @Service | 使用在service层类上用于实例化Bean |

| @Repository | 使用在dao层类上用于实例化Bean |

| @Autowired | 使用在字段上用于根据类型依赖注入 |

| @Qualifier | 结合@Autowired一起使用用于根据名称进行依赖注入 |

| @Resource | 相当于@Autowired+@Qualifier,按照名称进行注入 |

| @Value | 注入普通属性 |

| @Scope | 标注Bean的作用范围 |

| @PostConstruct | 使用在方法上标注该方法是Bean的初始化方法 |

| @PreDestroy | 使用在方法上标注该方法是Bean的销毁方法 |

注意:

使用注解进行开发时,需要在applicationContext.xml中配置组件扫描,作用是指定哪个包及其子包下的Bean需要进行扫描以便识别使用注解配置的类、字段和方法。

<context:component-scan base-package=“com.itheima”></context:component-scan>

使用@Compont或@Repository标识UserDaoImpl需要Spring进行实例化。

//@Component(“userDao”)

@Repository(“userDao”)

public class UserDaoImpl implements UserDao {

@Override

public void save() {

System.out.println(“save running… …”);

}

}

使用@Compont或@Service标识UserServiceImpl需要Spring进行实例化

使用@Autowired或者@Autowired+@Qulifier或者@Resource进行userDao的注入

//@Component(“userService”)

@Service(“userService”)

public class UserServiceImpl implements UserService {

/*@Autowired

@Qualifier(“userDao”)*/

@Resource(name=“userDao”)

private UserDao userDao;

@Override

public void save() {

userDao.save();

}

}

使用@Value进行字符串的注入

@Repository(“userDao”)

public class UserDaoImpl implements UserDao {

@Value(“注入普通数据”)

private String str;

@Value(“${jdbc.driver}”)

private String driver;

@Override

public void save() {

System.out.println(str);

System.out.println(driver);

System.out.println(“save running… …”);

}

}

使用@Scope标注Bean的范围

//@Scope(“prototype”)

@Scope(“singleton”)

public class UserDaoImpl implements UserDao {

//此处省略代码

}

使用@PostConstruct标注初始化方法,使用@PreDestroy标注销毁方法

@PostConstruct

public void init(){

System.out.println(“初始化方法…”);

}

@PreDestroy

public void destroy(){

System.out.println(“销毁方法…”);

}

2.2 Spring新注解

使用上面的注解还不能全部替代xml配置文件,还需要使用注解替代的配置如下:

  • 非自定义的Bean的配置:

  • 加载properties文件的配置:context:property-placeholder

  • 组件扫描的配置:context:component-scan

  • 引入其他文件:

| 注解 | 说明 |

| — | — |

| @Configuration | 用于指定当前类是一个 Spring 配置类,当创建容器时会从该类上加载注解 |

| @ComponentScan | 用于指定 Spring 在初始化容器时要扫描的包。 作用和在 Spring 的 xml 配置文件中的 <context:component-scan base-package=“com.itheima”/>一样 |

| @Bean | 使用在方法上,标注将该方法的返回值存储到 Spring 容器中 |

| @PropertySource | 用于加载.properties 文件中的配置 |

| @Import | 用于导入其他配置类 |

@Configuration

在这里插入图片描述

@ComponentScan

在这里插入图片描述

@Import

在这里插入图片描述

@Configuration

@ComponentScan(“com.itheima”)

@Import({DataSourceConfiguration.class})

public class SpringConfiguration {

}

@PropertySource

在这里插入图片描述

@value

在这里插入图片描述

@PropertySource(“classpath:jdbc.properties”)

public class DataSourceConfiguration {

@Value(“${jdbc.driver}”)

private String driver;

@Value(“${jdbc.url}”)

private String url;

@Value(“${jdbc.username}”)

private String username;

@Value(“${jdbc.password}”)

private String password;

@Bean

在这里插入图片描述

@Bean(name=“dataSource”)

public DataSource getDataSource() throws PropertyVetoException {

ComboPooledDataSource dataSource = new ComboPooledDataSource();

dataSource.setDriverClass(driver);

dataSource.setJdbcUrl(url);

dataSource.setUser(username);

dataSource.setPassword(password);

return dataSource;

}

测试加载核心配置类创建Spring容器

在这里插入图片描述

package com.itheima.web;

import com.itheima.config.SpringConfiguration;

import com.itheima.service.UserService;

import org.springframework.context.ApplicationContext;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class UserController {

public static void main(String[] args) {

ApplicationContext app = new AnnotationConfigApplicationContext(SpringConfiguration.class);

UserService userService = app.getBean(UserService.class);

userService.save();

}

}

3. Spring整合Junit


3.1 原始Junit测试Spring的问题

在测试类中,每个测试方法都有以下两行代码:

ApplicationContext ac = new ClassPathXmlApplicationContext(“bean.xml”);

IAccountService as = ac.getBean(“accountService”,IAccountService.class);

这两行代码的作用是获取容器,如果不写的话,直接会提示空指针异常。所以又不能轻易删掉。

3.2 上述问题解决思路

  • 让SpringJunit负责创建Spring容器,但是需要将配置文件的名称告诉它

  • 将需要进行测试Bean直接在测试类中进行注入

3.3 Spring集成Junit步骤

① 导入spring集成Junit的坐标

② 使用@Runwith注解替换原来的运行期

③ 使用@ContextConfiguration指定配置文件或配置类

④ 使用@Autowired注入需要测试的对象

⑤ 创建测试方法进行测试

3.4 Spring集成Junit代码实现

①导入spring集成Junit的坐标

org.springframework

spring-test

5.0.2.RELEASE

junit

junit

4.12

test

在这里插入图片描述

②使用@Runwith注解替换原来的运行期

@RunWith(SpringJUnit4ClassRunner.class)

public class SpringJunitTest {

}

③使用@ContextConfiguration指定配置文件或配置类

@RunWith(SpringJUnit4ClassRunner.class)

//加载spring核心配置文件

//@ContextConfiguration(value = {“classpath:applicationContext.xml”})

//加载spring核心配置类

@ContextConfiguration(classes = {SpringConfiguration.class})

public class SpringJunitTest {

}

④使用@Autowired注入需要测试的对象

@RunWith(SpringJUnit4ClassRunner.class)

@ContextConfiguration(classes = {SpringConfiguration.class})

public class SpringJunitTest {

@Autowired

private UserService userService;

}

⑤创建测试方法进行测试

@RunWith(SpringJUnit4ClassRunner.class)

@ContextConfiguration(classes = {SpringConfiguration.class})public class SpringJunitTest {

@Autowired

private UserService userService;

@Test

public void testUserService(){

userService.save();

}

}

在这里插入图片描述

Spring集成Junit步骤

①导入spring集成Junit的坐标

②使用@Runwith注解替换原来的运行期

③使用@ContextConfiguration指定配置文件或配置类

④使用@Autowired注入需要测试的对象

⑤创建测试方法进行测试

三、Spring的AOP

=======================================================================

1.Spring 的 AOP 简介


1.1 什么是 AOP

AOPAspect Oriented Programming 的缩写,意思为面向切面编程,是通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。

AOP 是 OOP 的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

1.2 AOP 的作用及其优势

  • 作用:在程序运行期间,在不修改源码的情况下对方法进行功能增强

  • 优势:减少重复代码,提高开发效率,并且便于维护

1.3 AOP 的底层实现

实际上,AOP 的底层是通过 Spring 提供的的动态代理技术实现的。在运行期间,Spring通过动态代理技术动态的生成代理对象,代理对象方法执行时进行增强功能的介入,在去调用目标对象的方法,从而完成功能的增强。

1.4 AOP 的动态代理技术

常用的动态代理技术

  • JDK 代理 : 基于接口的动态代理技术

  • cglib 代理:基于父类的动态代理技术

在这里插入图片描述

1.5 JDK 的动态代理

①目标类接口

package com.itheima.proxy.jdk;

public interface TargetInterface {

public void save();

}

②目标类

package com.itheima.proxy.jdk;

public class Target implements TargetInterface {

@Override

public void save() {

System.out.println(“save running”);

}

}

③动态代理代码,调用代理对象的方法测试

package com.itheima.proxy.jdk;

import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Method;

import java.lang.reflect.Proxy;

public class ProxyTest {

public static void main(String[] args) {

// 创建目标对象

Target target = new Target();

// 获得增强对象

Advice advice = new Advice();

// 返回值是动态生成的代理对象

TargetInterface proxy = (TargetInterface) Proxy.newProxyInstance(

target.getClass().getClassLoader(), // 目标对象加载器

target.getClass().getInterfaces(),//目标对象相同的接口字节码对象数组

new InvocationHandler() {

// 调用代理对象的任何方法,实质执行的都是invoke方法

@Override

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

// 前置增强

advice.before();

Object invoke = method.invoke(target, args);// 执行目标方法

// 后置增强

advice.afterReturning();

return invoke;

}

}

);

// 调用代理对象的方法

proxy.save();

}

}

在这里插入图片描述

1.6 cglib 的动态代理

①目标类

package com.itheima.proxy.cjlib;

public class Target {

public void save() {

System.out.println(“save running…”);

}

}

②动态代理代码,调用代理对象的方法测试

package com.itheima.proxy.cjlib;

import org.springframework.cglib.proxy.Enhancer;

import org.springframework.cglib.proxy.MethodInterceptor;

import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class ProxyTest {

public static void main(String[] args) {

//目标对象

final Target target = new Target();

//增强对象

final Advice advice = new Advice();

//返回值 就是动态生成的代理对象 基于cglib

//1、创建增强器

Enhancer enhancer = new Enhancer();

//2、设置父类(目标)

enhancer.setSuperclass(Target.class);

//3、设置回调

enhancer.setCallback(new MethodInterceptor() {

public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {

advice.before(); //执行前置

Object invoke = method.invoke(target, args);//执行目标

advice.afterReturning(); //执行后置

return invoke;

}

});

//4、创建代理对象

Target proxy = (Target) enhancer.create();

proxy.save();

}

}

在这里插入图片描述

1.7 AOP 相关概念

Spring 的 AOP 实现底层就是对上面的动态代理的代码进行了封装,封装后我们只需要对需要关注的部分进行代码编写,并通过配置的方式完成指定目标的方法增强。

在正式讲解 AOP 的操作之前,我们必须理解 AOP 的相关术语,常用的术语如下:

  • Target(目标对象):代理的目标对象。要被增强的对象

  • Proxy (代理):一个类被 AOP 织入增强后,就产生一个结果代理类。

  • Joinpoint(连接点):所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点。可以被增强的方法

  • 🧨🧨 Pointcut(切入点) 🧨🧨:所谓切入点是指我们要对哪些 Joinpoint 进行拦截的定义。被增强的方法。切入点是连接点的一部分。

  • 🧨🧨 Advice(通知/ 增强) 🧨🧨:所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知。增强实现的方法

  • 🧨🧨 Aspect(切面) 🧨🧨:是切入点和通知(引介)的结合。

  • 🧨🧨 Weaving(织入) 🧨🧨:是指把增强应用到目标对象来创建新的代理对象的过程。spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入。

1.8 AOP 开发明确的事项

1.8.1 需要编写的内容
  • 编写核心业务代码(目标类的目标方法)

  • 编写切面类,切面类中有通知(增强功能方法)

  • 在配置文件中,配置织入关系,即将哪些通知与哪些连接点进行结合

1.8.2 AOP 技术实现的内容

Spring 框架监控切入点方法的执行。一旦监控到切入点方法被运行,使用代理机制,动态创建目标对象的代理对象,根据通知类别,在代理对象的对应位置,将通知对应的功能织入,完成完整的代码逻辑运行。

1.8.3 AOP 底层使用哪种代理方式

在 spring 中,框架会根据目标类是否实现了接口来决定采用哪种动态代理的方式。

1.9 知识要点

  • aop:面向切面编程

  • aop底层实现:基于JDK的动态代理 和 基于Cglib的动态代理

  • aop的重点概念:

① Pointcut(切入点):被增强的方法

② Advice(通知/ 增强):封装增强业务逻辑的方法

③ Aspect(切面):切点+通知

④ Weaving(织入):将切点与通知结合的过程

  • 开发明确事项:

① 谁是切点(切点表达式配置)

② 谁是通知(切面类中的增强方法)

③ 将切点和通知进行织入配置

2. 基于 XML 的 AOP 开发


2.1 快速入门

①导入 AOP 相关坐标

②创建目标接口和目标类(内部有切点)

③创建切面类(内部有增强方法)

④将目标类和切面类的对象创建权交给 spring

⑤在 applicationContext.xml 中配置织入关系

⑥测试代码

2.1.1 导入 AOP 相关坐标

org.springframework

spring-context

5.0.5.RELEASE

org.aspectj

aspectjweaver

1.8.13

在这里插入图片描述

2.1.2 创建目标接口和目标类(内部有切点)

package com.itheima.aop;

public class Target implements TargetInterface {

@Override

public void save() {

System.out.println(“save running”);

}

}

在这里插入图片描述

2.1.3 创建切面类(内部有增强方法)

package com.itheima.aop;

public class MyAspect {

public void before() {

System.out.println(“前置增强。。。”);

}

}

在这里插入图片描述

2.1.4 将目标类和切面类的对象创建权交给 spring

在这里插入图片描述

2.1.5 在 applicationContext.xml 中配置织入关系

这一步就是:告诉Spring框架,哪些方法(切点)需要进行哪些增强(前置、后置。。。)

导入aop命名空间

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

<beans xmlns=“http://www.springframework.org/schema/beans”

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

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

">

配置切点表达式和前置增强的织入关系

aop:config

<aop:aspect ref=“myAspect”>

<aop:before method=“before” pointcut=“execution(public void com.itheima.aop.Target.save())”/>

</aop:aspect>

</aop:config>

意思就是在调用Target中的save()方法的时候,会进行前置增强,增强方法为MyAspect中的before方法。

2.1.6 测试代码

@RunWith(SpringJUnit4ClassRunner.class)

@ContextConfiguration(“classpath:applicationContext.xml”)

public class AopTest {

@Autowired

private TargetInterface target;

@Test

public void test1(){

target.method();

}

}

2.1.7 测试结果

在这里插入图片描述

2.2 XML 配置 AOP 详解

2.2.1 切点表达式的写法

表达式语法:

execution([修饰符] 返回值类型 包名.类名.方法名(参数))

  • 访问修饰符可以省略

  • 返回值类型、包名、类名、方法名可以使用星号*****代表任意

  • 包名与类名之间一个点 . 代表当前包下的类,两个点 … 表示当前包及其子包下的类

  • 参数列表可以使用两个点 … 表示任意个数,任意类型的参数列表

例如:

execution(public void com.itheima.aop.Target.method())

execution(void com.itheima.aop.Target.*(…))

execution(* com.itheima.aop..(…))

execution(* com.itheima.aop….(…))

execution(* .*(…))

  1. execution(public void com.itheima.aop.Target.method()) 只指定Target类下的method方法

  2. execution(void com.itheima.aop.Target.*(…)) 指定Target类下的任意方法

  3. execution(* com.itheima.aop..(…)) 指定aop包下的任意类任意方法

  4. execution(* com.itheima.aop….(…)) 指定aop包及其子包下的任意类任意方法

  5. execution(* .*(…)) 任意

2.2.2 通知的类型

通知的配置语法:

<aop:通知类型 method=“切面类中方法名” pointcut=“切点表达式"></aop:通知类型>

在这里插入图片描述

| 名称 | 标签 | 说明 |

| — | — | — |

| 前置通知 | aop:before | 用于配置前置通知。指定增强的方法在切入点方法之前执行。 |

2021年Java中高级面试必备知识点总结

在这个部分总结了2019年到目前为止Java常见面试问题,取其面试核心编写成这份文档笔记,从中分析面试官的心理,摸清面试官的“套路”,可以说搞定90%以上的Java中高级面试没一点难度。

本节总结的内容涵盖了:消息队列、Redis缓存、分库分表、读写分离、设计高并发系统、分布式系统、高可用系统、SpringCloud微服务架构等一系列互联网主流高级技术的知识点。

目录:

(上述只是一个整体目录大纲,每个点里面都有如下所示的详细内容,从面试问题——分析面试官心理——剖析面试题——完美解答的一个过程)

部分内容:

对于每一个做技术的来说,学习是不能停止的,小编把2019年到目前为止Java的核心知识提炼出来了,无论你现在是处于什么阶段,如你所见,这份文档的内容无论是对于你找面试工作还是提升技术广度深度都是完美的。

不想被后浪淘汰的话,赶紧搞起来吧,高清完整版一共是888页,需要的话可以点赞+关注

ttp://www.springframework.org/schema/beans/spring-beans.xsd

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

">

配置切点表达式和前置增强的织入关系

aop:config

<aop:aspect ref=“myAspect”>

<aop:before method=“before” pointcut=“execution(public void com.itheima.aop.Target.save())”/>

</aop:aspect>

</aop:config>

意思就是在调用Target中的save()方法的时候,会进行前置增强,增强方法为MyAspect中的before方法。

2.1.6 测试代码

@RunWith(SpringJUnit4ClassRunner.class)

@ContextConfiguration(“classpath:applicationContext.xml”)

public class AopTest {

@Autowired

private TargetInterface target;

@Test

public void test1(){

target.method();

}

}

2.1.7 测试结果

在这里插入图片描述

2.2 XML 配置 AOP 详解

2.2.1 切点表达式的写法

表达式语法:

execution([修饰符] 返回值类型 包名.类名.方法名(参数))

  • 访问修饰符可以省略

  • 返回值类型、包名、类名、方法名可以使用星号*****代表任意

  • 包名与类名之间一个点 . 代表当前包下的类,两个点 … 表示当前包及其子包下的类

  • 参数列表可以使用两个点 … 表示任意个数,任意类型的参数列表

例如:

execution(public void com.itheima.aop.Target.method())

execution(void com.itheima.aop.Target.*(…))

execution(* com.itheima.aop..(…))

execution(* com.itheima.aop….(…))

execution(* .*(…))

  1. execution(public void com.itheima.aop.Target.method()) 只指定Target类下的method方法

  2. execution(void com.itheima.aop.Target.*(…)) 指定Target类下的任意方法

  3. execution(* com.itheima.aop..(…)) 指定aop包下的任意类任意方法

  4. execution(* com.itheima.aop….(…)) 指定aop包及其子包下的任意类任意方法

  5. execution(* .*(…)) 任意

2.2.2 通知的类型

通知的配置语法:

<aop:通知类型 method=“切面类中方法名” pointcut=“切点表达式"></aop:通知类型>

在这里插入图片描述

| 名称 | 标签 | 说明 |

| — | — | — |

| 前置通知 | aop:before | 用于配置前置通知。指定增强的方法在切入点方法之前执行。 |

2021年Java中高级面试必备知识点总结

在这个部分总结了2019年到目前为止Java常见面试问题,取其面试核心编写成这份文档笔记,从中分析面试官的心理,摸清面试官的“套路”,可以说搞定90%以上的Java中高级面试没一点难度。

本节总结的内容涵盖了:消息队列、Redis缓存、分库分表、读写分离、设计高并发系统、分布式系统、高可用系统、SpringCloud微服务架构等一系列互联网主流高级技术的知识点。

目录:

[外链图片转存中…(img-TmshgstB-1714394236216)]

(上述只是一个整体目录大纲,每个点里面都有如下所示的详细内容,从面试问题——分析面试官心理——剖析面试题——完美解答的一个过程)

[外链图片转存中…(img-7Op6eHo1-1714394236217)]

部分内容:

[外链图片转存中…(img-7uoRLWTF-1714394236217)]

[外链图片转存中…(img-MMsuMEae-1714394236217)]

[外链图片转存中…(img-cSTVuiGs-1714394236217)]

对于每一个做技术的来说,学习是不能停止的,小编把2019年到目前为止Java的核心知识提炼出来了,无论你现在是处于什么阶段,如你所见,这份文档的内容无论是对于你找面试工作还是提升技术广度深度都是完美的。

不想被后浪淘汰的话,赶紧搞起来吧,高清完整版一共是888页,需要的话可以点赞+关注

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值