2022年1月14日19:34:19
Spring框架是 Java 平台的一个开源的全栈(full-stack)应用程序框架和控制反转容器实现,Spring框架以 Apache License 2.0 开源许可协议的形式发布,该框架最初由 Rod Johnson 以及 Juergen Hoeller 等人开发。
控制反转(IOC,Inverse of contril),即把创建对象的权利交给框架,也就是将对象的创建、对象的存储、对象的管理交给了Spring容器
在pox.xml
文件中导入Spring依赖
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webmvcartifactId>
<version>5.2.19.RELEASEversion>
dependency>
和普通项目一样创建一个JavaBean结构(需要提供set方法,Spring通过Set注入)
public class User {
private String name;
private String sex;
private Integer age;
public void setName(String name) {
this.name = name;
}
public void setSex(String sex) {
this.sex = sex;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", sex='" + sex + '\'' +
", age=" + age +
'}';
}
public void show() {
System.out.println(this.toString());
}
}
@Test
public void show() {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
User user = (User) context.getBean("user");
user.show();
}
类型名 | 简介 |
---|---|
ClassPathXmlApplicationContext | 通过读取类路径下的 XML 格式的配置文件创建 IOC 容器对象 |
FileSystemXmlApplicationContext | 通过文件系统路径读取 XML 格式的配置文件创建 IOC 容器对象 |
ConfigurableApplicationContext | ApplicationContext 的子接口,包含一些扩展方法 refresh() 和 close() ,让 ApplicationContext 具有启动、关闭和刷新上下文的能力。 |
WebApplicationContext | 专门为 Web 应用准备,基于 Web 环境创建 IOC 容器对象,并将对象引入存入 ServletContext 域中。 |
<bean id="user1" class="com.august.pojo.User.User" >
<constructor-arg index="0" value="111" />
<constructor-arg name="sex" value="女" />
<constructor-arg value="1"/>
bean>
private String name;
private String email;
private Address address;
private String[] books;
private List<String> hobbys;
private Map<String,String> cards;
private Set<String> games;
private Properties properties;
xml:
<bean id="address" class="com.august.pojo.User.Address">
<property name="address" value="北京"/>
<property name="roadNumber" value="4477777"/>
bean>
<bean id="person" class="com.august.pojo.User.Person">
<property name="name" value="小明"/>
<property name="email">
<null/>
property>
<property name="address" ref="address"/>
<property name="books">
<array>
<value>>]]>value>
<value>>]]>value>
<value>>]]>value>
<value>>]]>value>
array>
property>
<property name="hobbys">
<list>
<value>吃饭value>
<value>睡觉value>
<value>打豆豆value>
list>
property>
<property name="cards">
<map>
<entry key="身份证" value="45277555577777"/>
<entry key="银行卡" value="6888454544466"/>
<entry key="电话卡" value="13888888888"/>
map>
property>
<property name="games">
<set>
<value>LOLvalue>
<value>CFvalue>
<value>TGA5value>
set>
property>
<property name="properties">
<props>
<prop key="driver">mysqlprop>
<prop key="username">123456prop>
<prop key="url">localhostprop>
props>
property>
bean>
<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:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="user" class="com.august.pojo.User.User" p:age="18" p:name="小王" p:sex="男"/>
<bean id="userA" class="com.august.pojo.User.User" c:age="18" c:name="小明" c:sex="女"/>
beans>
注意:在使用P命名空间C命名空间时候需要导入约束;
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
说明: p 指的是 property
提供set方法
c 指的是 constructor-arg
,所以在使用c命名空间时,需要提供有参构造器
xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd
<context:component-scan base-package="com.august"/>
@Repository
、@Service
和@Controller
是@Component
的特化,用于更具体的用例(分别在持久层,服务层和表示层中)。
注意:虽然它们本质上一样,但是为了代码的可读性,为了程序结构严谨我们肯定不能随便胡乱标记。
<context:component-scan
base-package="com.august"
resource-pattern="Soldier*.class"/>
<context:component-scan base-package="com.august">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
context:component-scan>
<context:component-scan base-package="com.august" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
context:component-scan>
①创建配置类
@Configuration
public class MyConfiguration {
}
②根据配置类创建IOC容器对象
@Test
public void Test() {
//AnnotationConfigApplicationContext 根据配置类创建 IOC 容器对象
ApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class);
}
方式一:
@Configuration
public class MyConfiguration {
// @Bean 注解相当于 XML 配置文件中的 bean 标签
// @Bean 注解标记的方法的返回值会被放入 IOC 容器
// 默认以方法名作为 bean 的 id
@Bean("user")
public User getUser(){
User user = new User();
user.setName("xiaoMing");
return user;
}
}
方式二:
自己在交给Spring管理类上加注解@Component
@Component
public class User {
....
}
@Configuration
@ComponentScan("com.august")
public class MyConfiguration {
……
}
条件: 参与自动装配的组件(需要装配别人、被别人装配)全部都必须在IOC容器中。
声明级别: 字段(推荐) (不需要提供setXxx()方法)、构造器 、set方法
注入(装配)规则:默认按照属性值Class类型实现装配,如果接口仅有一个实现注入时默认注入对应实现类如果接口存在多个实现此时需要应用程序声明注入的Bean (使用@Qualifier(value="实现类id")
)
通常和@Autowired
使用
@AutoWried按by type自动注入,@Resource默认按byName自动注入。
默认按 byName自动注入,如果找不到再按byType找bean,如果还是找不到则抛异常,无论按byName还是byType如果找到多个,则抛异常。
注入属性值
@Component
public class User {
@Value("张三")
private String name;
@Value("16")
private String sex;
@Value("18")
private Integer age;
}
返回单例或者新例
面向切面编程(AOP,Aspect oriented Programming) 就是将那些与业务无关,却为业务模块所共同调用的逻辑或责任分开封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。
所谓连接点是指那些被拦截到的点(可以被拦截代理的方法)。在Spring中,这些点指的是方法,Spring只支持方法类型的连接点
定位连接点的方式。
相当于特定的连接点(JoinPoint),AOP通过“切入点"来定位特定的连接点。连接点可以类别为数据库中的记录,而切入点相当于查询条件(在Spring AOP中使用一套特定的切入点表达式来描述)。切入点和连接点不是一对一的关系,一个切入点可以匹配多个连接点。
每一个横切关注点上要做的事情都需要写一个方法来实现,这样的方法就叫通知方法。
封装通知方法的类。(引入增强方法)
被代理的目标对象。
向目标对象应用通知之后创建的代理对象。
AspectJ依赖:
<dependency>
<groupId>org.aspectjgroupId>
<artifactId>aspectjweaverartifactId>
<version>1.9.7version>
dependency>
<dependency>
<groupId>org.aspectjgroupId>
<artifactId>aspectjweaverartifactId>
<version>1.9.7version>
dependency>
提供接口
public interface Calculator {
int add(int i,int j);
int sub(int i,int j);
int mul(int i,int j);
int div(int i,int j);
}
接口实现类
在 Spring 下工作,所有的一切都必须放在 IOC 容器中。现在接口的实现类是 AOP 要代理的目标类,所以它也必须放入 IOC 容器。
public class calculatorImpl implements Calculator {
@Override
public int add(int i, int j) {
int result = i + j;
System.out.println("方法内部add result = " + result);
return result ;
}
@Override
public int sub(int i, int j) {
int result = i - j;
System.out.println("方法内部sub result = " + result);
return result;
}
@Override
public int mul(int i, int j) {
int result = i * j;
System.out.println("方法内部mul result = " + result);
return result;
}
@Override
public int div(int i, int j) {
int result = i / j;
System.out.println("方法内部div result = " + result);
return result;
}
}
package com.august.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
/**
* 创建切面类
*
* @author : Crazy_August
* @description :
* @Time: 2022-01-20 18:30
*/
@Aspect // @Aspect表示这个类是一个 切面类
@Component // @Component 注解保证这个切面类能够放入IOC容器
public class LogAspect {
//切入点
@Pointcut(value="execution(* com.august.aop.impl..*(..))")
public void cut(){}
@Before(value="cut()")
public void before(){
System.out.println("前置通知....");
}
@After(value="cut()")
public void after(){
System.out.println("后置通知....");
}
@AfterThrowing(value="cut()")
public void afterThrowing(){
System.out.println("异常通知....");
}
@Around(value="cut()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("环绕前通知....");+
//执行目标通知
Object result = pjp.proceed();
System.out.println("环绕后通知....");
return result;
}
@AfterReturning(value="cut()")
public void afterReturning(){
System.out.println("返回通知....");
}
}
<beans
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation=" http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<aop:aspectj-autoproxy />
<context:component-scan base-package="com.august" />
beans>
public class CalculatorTest {
private ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
@Test
public void add() {
//注意:用接口接收,动态代理不明确是哪个类
Calculator calculator = (Calculator)context.getBean("calculatorImpl");
calculator.add(1, 5);
}
}
正常输出结果:
环绕前通知…
前置通知…
方法内部add result = 6
返回通知…
后置通知…
环绕后通知…
异常输出结果:
环绕前通知…
前置通知…
方法内部add result = 6
异常通知…
后置通知…
1、JoinPoint接口
org.aspectj.lang.JoinPoint
// @Before注解标记前置通知方法
// value属性:切入点表达式,告诉Spring当前通知方法要套用到哪个目标方法上
// 在前置通知方法形参位置声明一个JoinPoint类型的参数,Spring就会将这个对象传入
// 根据JoinPoint对象就可以获取目标方法名称、实际参数列表
@Before(value = "execution(public int com.atguigu.aop.api.Calculator.add(int,int))")
public void printLogBeforeCore(JoinPoint joinPoint) {
// 1.通过JoinPoint对象获取目标方法签名对象
// 方法的签名:一个方法的全部声明信息
Signature signature = joinPoint.getSignature();
// 2.通过方法的签名对象获取目标方法的详细信息
String methodName = signature.getName();
System.out.println("methodName = " + methodName);
int modifiers = signature.getModifiers();
System.out.println("modifiers = " + modifiers);
String declaringTypeName = signature.getDeclaringTypeName();
System.out.println("declaringTypeName = " + declaringTypeName);
// 3.通过JoinPoint对象获取外界调用目标方法时传入的实参列表
Object[] args = joinPoint.getArgs();
// 4.由于数组直接打印看不到具体数据,所以转换为List集合
List<Object> argList = Arrays.asList(args);
System.out.println("[AOP前置通知] " + methodName + "方法开始了,参数列表:" + argList);
}
需要获取方法签名、传入的实参等信息时,可以在通知方法声明JoinPoint类型的形参。
methodName = add
modifiers = 1025
declaringTypeName = com.august.aop.Calculator
[AOP前置通知] add方法开始了,参数列表:[1, 5]
2、方法返回值
// @AfterReturning注解标记返回通知方法
// 在返回通知中获取目标方法返回值分两步:
// 第一步:在@AfterReturning注解中通过returning属性设置一个名称
// 第二步:使用returning属性设置的名称在通知方法中声明一个对应的形参
@AfterReturning(
value = "execution(public int com.atguigu.aop.api.Calculator.add(int,int))",
returning = "targetMethodReturnValue"
)
public void printLogAfterCoreSuccess(JoinPoint joinPoint, Object targetMethodReturnValue) {
String methodName = joinPoint.getSignature().getName();
System.out.println("[AOP返回通知] "+methodName+"方法成功结束了,返回值是:" + targetMethodReturnValue);
}
3、目标方法抛出的异常
// @AfterThrowing注解标记异常通知方法
// 在异常通知中获取目标方法抛出的异常分两步:
// 第一步:在@AfterThrowing注解中声明一个throwing属性设定形参名称
// 第二步:使用throwing属性指定的名称在通知方法声明形参,Spring会将目标方法抛出的异常对象从这里传给我们
@AfterThrowing(
value = "execution(public int com.atguigu.aop.api.Calculator.add(int,int))",
throwing = "targetMethodException"
)
public void printLogAfterCoreException(JoinPoint joinPoint, Throwable targetMethodException) {
String methodName = joinPoint.getSignature().getName();
System.out.println("[AOP异常通知] "+methodName+"方法抛异常了,异常类型是:" + targetMethodException.getClass().getName());
}
1.声明:
在一处声明切入点表达式之后,其他有需要的地方引用这个切入点表达式。易于维护,一处修改,处处生效。声明方式如下:
// 切入点表达式重用
//切入点
@Pointcut(value="execution(* com.august.aop.impl.*.*(..))")
public void pointCut() {}
1.1同一个类内部引用
@Before(value="pointCut()")
public void before(){
System.out.println("前置通知....");
}
1.2在不同类中引用
@After(value="com.august.aspect.LogAspect.pointCut()")
public void after(){
System.out.println("后置通知....");
}
2.集中管理
创建存放切入点表达式的类,可以把整个项目中所有切入点表达式全部集中过来,便于统一管理:
@Component
public class MyPointCut {
@Pointcut(value = "execution(public int *..Calculator.sub(int,int))")
public void atguiguGlobalPointCut(){}
@Pointcut(value = "execution(public int *..Calculator.add(int,int))")
public void atguiguSecondPointCut(){}
@Pointcut(value = "execution(* *..*Service.*(..))")
public void transactionPointCut(){}
}
概念: 相同目标方法上同时存在多个切面时,切面的优先级控制切面的内外嵌套顺序。
使用 @Order 注解可以控制切面的优先级:
1、2步与注解相同
3.配置Spring配置文件
<bean id="calculatorPure" class="com.atguigu.aop.imp.CalculatorPureImpl"/>
<bean id="logAspect" class="com.atguigu.aop.aspect.LogAspect"/>
<aop:config>
<aop:pointcut id="logPointCut" expression="execution(* *..*.*(..))"/>
<aop:aspect ref="logAspect">
<aop:before method="printLogBeforeCore" pointcut-ref="logPointCut"/>
<aop:after-returning
method="printLogAfterCoreSuccess"
pointcut-ref="logPointCut"
returning="targetMethodReturnValue"/>
<aop:after-throwing
method="printLogAfterCoreException"
pointcut-ref="logPointCut"
throwing="targetMethodException"/>
<aop:after method="printLogCoreFinallyEnd" pointcut-ref="logPointCut"/>
aop:aspect>
aop:config>