我的Java-Web进阶--Spring
1.Spring框架概述
2.IOC控制反转(非注解版)
IOC的基础操作
Spring的IoC(Inversion of Control,控制反转)是Spring框架的核心特性之一。它允许对象在创建时由一个调节器对象或服务提供它们的依赖,而不是通过硬编码的方式创建这些依赖。这促进了松耦合的设计,使得应用程序更易于测试和维护。
下面我将按照您的要求介绍如何使用XML配置方式来实现Spring的IoC基础操作,并给出实例代码。
1. 创建相关类
首先,我们创建几个简单的Java类,这些类将作为Spring容器中的Bean。
// User.java
package com.example;public class User {private String name;private int age;private String[] hobbies;// Constructors, getters and setterspublic User() {}public User(String name, int age, String[] hobbies) {this.name = name;this.age = age;this.hobbies = hobbies;}@Overridepublic String toString() {return "User{name='" + name + "', age=" + age + ", hobbies=" + String.join(", ", hobbies) + "}";}
}
2. 添加Spring的XML配置文件
接下来,我们需要创建一个Spring的XML配置文件来定义Bean。这个文件通常位于src/main/resources
目录下,命名为applicationContext.xml
。
<!-- 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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><!-- Bean definition for User with constructor arguments --><bean id="user" class="com.example.User"><constructor-arg value="John Doe" type="String"/><constructor-arg value="30" type="int"/><constructor-arg><array><value>Reading</value><value>Hiking</value><value>Coding</value></array></constructor-arg></bean></beans>
在这个配置文件中,我们定义了一个名为user
的Bean,它使用了User
类的构造函数进行初始化。这里展示了如何为构造函数参数传递String
、int
以及String[]
类型的值。
3. 注册Bean并使用
最后,我们需要编写一些Java代码来加载Spring上下文并获取Bean实例。
// MainApp.java
package com.example;import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class MainApp {public static void main(String[] args) {// Load Spring configuration fileApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");// Retrieve bean from Spring containerUser user = (User) context.getBean("user");// Print out the retrieved beanSystem.out.println(user);}
}
这段代码创建了一个ClassPathXmlApplicationContext
实例,它会从类路径下加载applicationContext.xml
配置文件。然后,我们使用getBean
方法根据ID检索User
类型的Bean,并将其打印出来。
XML的Bean对象的参数设置
property
property通过调用类的setter方法进行参数设置
constructor-arg
constructor-arg通过调用构造方法给对象赋值
constructor-arg用于集合注入
Bean对象生命周期
3.IOC控制反转(半注解版)
Spring框架的注解开发是一种基于元数据的配置方式,它允许开发者通过在类、方法或字段上添加注解来替代XML配置。这种方式不仅简化了配置,还使得代码更加清晰和易于维护。以下是几个常用的Spring注解及其用途。
Bean对象池查询
Bean对象创建@Component
不写@Component里面的id,默认把类型作为id
简单的数据类型传参@Value
对象类型数据传参@Autowired,@Qualified,@Resource
@Autowired
@Autowired
用于自动装配依赖,它可以通过构造函数、setter方法或直接在字段上使用。默认情况下,@Autowired
是基于类型进行匹配的。如果容器中有多个相同类型的Bean,而没有指定具体的Bean,则可能会抛出异常。
package com.example;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;@Component
public class ServiceA {private final Repository repository;// 构造器注入@Autowiredpublic ServiceA(Repository repository) {this.repository = repository;}// 使用服务public void useService() {System.out.println("Using " + repository);}
}
@Qualifier
@Qualifier
与 @Autowired
一起使用,当有多个相同类型的Bean时,可以通过名称明确指定要注入哪个Bean。这解决了类型匹配时可能存在的歧义问题。
package com.example;import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;@Component
public class ServiceB {private final Repository specificRepository;// 构造器注入,并使用 @Qualifier 指定特定的 Bean@Autowiredpublic ServiceB(@Qualifier("specificRepository") Repository specificRepository) {this.specificRepository = specificRepository;}// 使用服务public void useService() {System.out.println("Using " + specificRepository);}
}
@Resource
@Resource
是Java EE提供的注解,也支持按名称进行依赖注入。它首先尝试根据名称查找Bean,如果找不到,则回退到按类型查找。因此,它总是优先考虑名字匹配,这与@Autowired
的行为有所不同。
package com.example;import javax.annotation.Resource;
import org.springframework.stereotype.Component;@Component
public class ServiceC {// 字段注入,使用@Resource按名称查找@Resource(name="specificRepository")private Repository specificRepository;// 使用服务public void useService() {System.out.println("Using " + specificRepository);}
}
理解:Spring把注册的对象都放到一个对象池里面,通过id匹配
@Autowired注解只通过匹配相同对象类型的方式,创建某一大对象的时候,把相同参数的小对象作为参数传递
@Qualified注解通过匹配id的方式,把匹配到的小对象赋值给大对象
@Resource注解先匹配id,如果匹配不到则匹配相同类型的
Bean对象生命周期@Scope
4.IOC控制反转(全注解版)
Bean对象池查询
5.AOP面向切面编程
面向切面编程(AOP, Aspect-Oriented Programming)是Spring框架中的一个重要特性,它允许你将横切关注点(如日志记录、事务管理、安全性等)从业务逻辑中分离出来。AOP通过在方法执行前后插入额外的行为来实现这一点,而不需要修改原有代码。使用基于注解的AOP配置可以简化配置过程,并使代码更加清晰和易于维护。
AOP 基于注解的使用方法
1. 引入依赖
首先,确保你的项目包含了必要的Spring AOP依赖。如果你使用的是Maven,可以在pom.xml
中添加以下依赖:
<dependencies><!-- Spring AOP --><dependency><groupId>org.springframework</groupId><artifactId>spring-aop</artifactId><version>5.3.10</version> <!-- 使用与你的项目兼容的版本 --></dependency><!-- 如果需要使用AspectJ编译器 --><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.7</version> <!-- 使用与你的项目兼容的版本 --></dependency>
</dependencies>
2. 启用AOP支持
为了让Spring能够识别和处理AOP注解,你需要在配置类上启用AOP支持。这可以通过在配置类上添加@EnableAspectJAutoProxy
注解来完成。
package com.example.config;import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;@Configuration
@EnableAspectJAutoProxy
public class AppConfig {// 配置类内容
}
3. 创建切面类(重点)
接下来,创建一个切面类,并使用@Aspect
注解将其标记为一个切面。在这个类中,你可以定义各种通知(Advice),比如@Before
, @After
, @Around
, @AfterReturning
, 和 @AfterThrowing
,它们分别对应于方法执行前、后、环绕、成功返回后和抛出异常后。
package com.example.aspect;import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;@Aspect
@Component
public class LoggingAspect {// 定义切入点表达式@Pointcut("execution(* com.example.service.*.*(..))")public void serviceLayer() {}// 在服务层方法执行前记录日志@Before("serviceLayer()")public void logBefore() {System.out.println("Logging before method execution.");}// 在服务层方法执行后记录日志@After("serviceLayer()")public void logAfter() {System.out.println("Logging after method execution.");}
}
4. 定义业务逻辑类
现在,我们可以定义一些业务逻辑类,这些类的方法将会被上面定义的切面所拦截。
package com.example.service;import org.springframework.stereotype.Service;@Service
public class MyService {public void performTask() {System.out.println("Executing task in MyService.");}
}
5. 测试AOP功能
最后,我们编写一个简单的测试类来验证AOP是否按预期工作。
package com.example;import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;import com.example.service.MyService;public class AopTestApp {public static void main(String[] args) {ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);MyService myService = context.getBean(MyService.class);// 调用服务方法,触发AOP通知myService.performTask();}
}
6. 运行结果
当你运行AopTestApp
时,你应该会看到如下输出:
Logging before method execution.
Executing task in MyService.
Logging after method execution.
这表明AOP切面已经成功地在MyService#performTask
方法执行前后插入了日志记录的通知。
AOP 注解详解
- @Aspect:用于定义一个类作为切面。
- @Pointcut:用于定义切入点表达式,标识哪些连接点(通常是方法调用)会被拦截。可以重复使用。
- @Before:用于定义前置通知,在方法执行之前执行。
- @After:用于定义后置通知,在方法执行之后执行,无论方法是否抛出了异常。
- @Around:用于定义环绕通知,可以完全控制方法的执行流程,包括选择是否继续执行方法。
- @AfterReturning:用于定义返回后通知,在方法正常返回后执行。
- @AfterThrowing:用于定义异常通知,在方法抛出异常后执行。
切入点表达式
切入点表达式是用来指定哪些方法应该被拦截的规则。AspectJ提供了丰富的语法来定义切入点表达式。例如:
execution(* com.example.service.*.*(..))
匹配com.example.service
包下所有类的所有方法。within(com.example.service..*)
匹配com.example.service
包及其子包下的所有类的所有方法。@annotation(org.springframework.transaction.annotation.Transactional)
匹配所有带有@Transactional
注解的方法。
通过合理使用这些注解和表达式,你可以轻松地为应用程序添加横切关注点,同时保持业务逻辑的清晰和简洁。
通知执行顺序
基于XML的配置方式(非重点)