1.Spring容器的概念
Spring是一个轻量级的框架,可以解决企业开发的复杂性,让开发效率提升,他核心的两个点是:
1.IOC
IOC:在java中,我们程序员一般是去创建一个对象,那么有个问题就是耦合性太高了,在后期的修改和维护的维修成本比较大,在IOC中,对象的创建依赖注入是由容器负责,而不是由程序员直接控制,他是通过dl的注入方式,来实现对象直接的解耦,提高了代码的灵活性和可维护性
2.AOP
AOP:AOP也被称为面向切面,他可以在不改变原代码对功能进行增强
1.2,Spring入门
首先我们要在pom.xml中添加spring的依赖
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.24</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.24</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<!-- <scope>test</scope>-->
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.23</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.49</version>
</dependency>
</dependencies>
在这里我添加了mysql的依赖,大家也可以先添加上去,在后面我们的案例中会用到,如果下载失败或者网络原因下载中断,或者还不会配置maven的可以参考我之前写的
https://blog.csdn.net/m0_70812512/article/details/139581648?spm=1001.2014.3001.5502
在这里就会有你spring的依赖项
如果说你去创建xml没有这个图标说明你spring依赖没有配置好。
好接下来我们通过几个案例来了解到这个spring注入的方式
1.通过xml实现注入
通过我们去拿取这个对象中的方法时都是去创建他,这个会很繁琐,而且后期维护性也不高
那么我们就可以通过spring将对象交给spring去管理,让spring帮我们去注入
1.set注入
首先看这里,在这里我们创建了一个user类,在这个类中我修饰了一个name,并且为这个name提供了一个set方法,用于注入值,并且提供了一个构造函数,那么他现在是符合springbean注入的条件
现在我通过xml来注入,我们定义了一个bean的标签,并且指定了他所以管理的对象是User,在property中我们指定了User中的属性名name,并且为name赋值
现在我们来测试看看会是什么结果
// 加载自定义的spring配置文件
ApplicationContext context = new ClassPathXmlApplicationContext("beans2.xml");
// 获取配置文件对象User
User user = context.getBean("user",User.class);
// 操作user对象
// System.out.println(user);
user.add();
在这里我们可以看到,我们告诉了spring是那一个配置文件,那他就会根据你配置文件中所定义的bean去构造这个对象,在通过getbean获取到这个对象(我们给每个bean都设置了id),并且调用其中的方法,这样就不在是我们程序员手动创建对象了,而是交给spring管理,让他帮我们来管理这个对象
<!-- 属性注入方式-->
<bean id="user2" class="com.zking.Utile.User">
<property name="name" value="谭建涛"></property>
</bean>
<!-- 构造注入方式-->
<bean id="user3" class="com.zking.Utile.User">
<constructor-arg name="name" value="有参构造注入方式"></constructor-arg>
</bean>
<!-- p注入方式-->
<bean id="user4" class="com.zking.Utile.User" p:name="p注入方式">
</bean>
当然这里还有一些其他的注入方式大家也可以去试试,在这里我们就不全部展示出来了,他们的意思都一样,就是通过springIOC来管理这个创建对象的过程,我们去拿就行
接下来我们来按照我们经常写代码的一些主要配置来搞几个小案例
我们看这里,我有一个数据访问层和一个业务逻辑层,在业务逻辑层中构造了数据访问层的这个对象,并且里面提供了一个
User_Sevsiver
方法,在这个方中可以去直接实现我们UserDao中的执行语句
好,现在我们来看xml,我将Userdao和UserServer都交给spring管理,并且设置了一个不同的id
在UserServier中就会去注入这个UserDao(上面id是UserDao),那既然我们已经注入了UserDao在这个set方法中了,那我在去调UserServier中的
User_Sevsiver
方法,他不就是会去执行到UserDao中的方法吗
实现了吧,是吧,其他案例我就不一一写了,基本都都大差不差,其实就是原来我们要直接去创建对象,现在不用了,给spring去管理,我们拿就好了,spring通过dl注入实现IOC控制反转
在说一个吧,spring自动装配
在这里我们将UserDao和UserSever交给spring管理,并且在 后面我们添加了autowire="byName",这个的意思是springIOC中的一个自动装配的一方式,他会扫描ImpUserServer中的属性值,比如说是userdao,那么他就会尝试将userdao注入到usersever这个bean当中,就可以实现spring的自动装配
1.3.通过组件扫描的方式实现
虽然说这种xml声明的方式确实可以实现我们的IOC控制反转,但是这个步骤什么的到处配置还是会有点麻烦,那么spring肯定还有其他的方式来实现这个控制反转,那就是组件扫描
1.什么是组件扫描
我们来看这个张图,我们原来是不是声明告诉spring,我指定那些要注入的东西那我们在根据他这个id去调这个bean,就可以实现这个方法了吧。
那有没有一种方式就是说,我可以给每一个对象中去声明一个标识符,在让spring通过标识符来识别变成一个bean,并且扫描不是指定某一个类来实现,而是去扫描结构下的所以类,当然有,我们来看上面的图片,我们指定开起了组件扫描,扫描com.zking下面的所以类
<?xml version="1.0" encoding="UTF-8"?>
<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">
<!-- 开启组件扫描,当前扫描com.zking下的全部组件-->
<!-- <context:component-scan base-package="com.zking"></context:component-scan>-->
<!-- 是 Spring 的注解驱动配置之一,用于自动扫描指定包下的组件,-->
<!-- 如带有 @Component, @Repository, @Service, @Controller 等注解的类,并将其注册为 Spring 的 Bean。-->
<context:component-scan base-package="com.zking"></context:component-scan>
</beans>
这里的
@Component, @Repository, @Service, @Controller
就是我们今天要说的通过注解的方式实现DL注入
首先我们先来了解这是是什么,首先要知道spring就是通过识别出com.zking下面的所以包含这些注解的来作为spring的bean,那有了这个bean,他就可以通过这个bean去里面拿这个对象去实现这个方法。
@Component:是spring当中的一个注解,他一般是拥有业务逻辑层,你可以理解为他将自己变成了一个spring可以扫描识别到变成一个bean
@Repository:一般用于数据访问层,他就是告诉spring我这个类是用于数据访问的
@Service:一看名字就知道了吧,他其实就是告诉spring他是业务逻辑层
@Controller:这个注解标记的类是一个 Spring MVC 控制器组件,用于处理 HTTP 请求和响应。它扩展了
@Component
注解,通常与 Spring MVC 框架结合使用,用于构建 Web 应用。他们都可以和
@Autowired
或@Resource
等注解来自动装配 Bean
@Autowired:是spring的自动注解,我们声明的全局变量中
在这里我们创建了一个ImpUser_Dao类,并且将他标记为
@Repository("userDao")spring中的组件,这里这个的意思是为相当于注入对象的name属性,可以理解为id,默认是这个类小写,impUser_Dao,那么在 ImpUserSever_Dao 中,我们设置为自动注入@Autowired,在UserDao userDao = null;userdao要和你那个dao里面的@Repository("userDao")值一致,因为他是通过你这个name的属性值在com.zking下去扫描,去找,找到了就将这个对象注入进去,在最后我们就可以通过
UserSever serv = apl.getBean("impUserSever_Dao",UserSever.class);
去创建这个类并且调用他里面的方法(默认是首字母小写),那么他就会自动的将Userdao注入进去,我们在调里面的方法是也就可以执行到数据访问逻辑层中了
那么这种其实还是依赖了xml,如果不想依赖xml,完全注解开发
package com.zking.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
//标记当前类为配置类
@Configuration
// 开启组件扫描
@ComponentScan({"com.zking"})
//引入外部配置文件
@PropertySource("db.properties")
public class SpringUserConfig {
}
我们可以通过创建一个java类,在类中将他声明为他是配置文件
在通过
ApplicationContext apl = new AnnotationConfigApplicationContext(SpringUserConfig.class);
作引入,是相同的效果。
3.AOP底层原理和实现
1.AOP是什么
简单来说aop就说在不改变原代码时,对原来的功能增强
AOP也被称为面向切面,切面的意思是为他是由我们定义的切点组成的,可以理解为我现在有两个类,一个userdao,一个Studentdao,他们下面分别有add,update方法,那么他们类里面的方法又可以叫切点,那么我们就可以定义这个切点为一个切面,对我们的功能代码增强。
2.AOP的底层原理
就是动态代理,动态代理又可以分为
有接口的情况:JDK动态代理
无接口情况:CGLIB动态代理
1.有接口的情况:JDK动态代理
package com.zking.dao.imp;
import com.zking.dao.UserDao;
import org.springframework.stereotype.Repository;
import javax.annotation.Resource;
@Repository
public class ImpUserDao implements UserDao {
@Override
public void add() {
System.out.println("数据添加");
}
@Override
public void Update() {
System.out.println("数据修改");
}
}
创建Userdao接口代理对象
2.无接口情况:CGLIB动态代理
package com.zking.proxy.CGilb;
public class User {
public void add(){
System.out.println("CGLIB方式");
}
public static class person extends User{
public void add(){
System.out.println("CGLIB开启事务");
super.add();
System.out.println("CGLIB事务提交");
}
}
}
没有接口,而是直接继承这个类中的方法在方法之前作增强
java动态代理
package com.zking.proxy;
import com.zking.dao.UserDao;
/**
* 用户Dao代理类,用于在不修改原有UserDao实现的基础上,添加额外的功能(如日志、事务等)。
*/
public class UserDaoproxy2 {
private UserDao userDao;
/**
* 构造函数,传入UserDao实例,代理类将围绕这个实例进行操作。
*
* @param userDao 需要被代理的UserDao实例
*/
public UserDaoproxy2(UserDao userdao) {
this.userDao = userdao;
}
/**
* 创建UserDao的代理实例。
*
* @return 返回代理后的UserDao实例
*/
public UserDao newProxyInstance() {
System.out.println("代理类");
// 使用Java反射机制创建代理实例,代理实现类为UserInvoaction
return (UserDao) java.lang.reflect.Proxy.newProxyInstance(userDao.getClass().getClassLoader(), userDao.getClass().getInterfaces(), new UserInvoaction());
}
/**
* InvocationHandler实现类,用于处理代理对象的方法调用。
*/
class UserInvoaction implements java.lang.reflect.InvocationHandler {
/**
* 当调用代理对象的方法时,实际上会执行此方法。
*
* @param proxy 代理对象
* @param method 被调用的方法
* @param args 方法参数
* @return 方法返回值
* @throws Throwable 方法执行中抛出的异常
*/
public Object invoke(Object proxy, java.lang.reflect.Method method, Object[] args) throws Throwable {
System.out.println("前置增强");
// 调用原始UserDao实例的方法
Object obj = method.invoke(userDao, args);
System.out.println("后置增强");
// 返回方法执行结果
return obj;
}
}
}
4.AOP准备工作
Aspectl介绍
在spring当中一般都在基于Aspectl实现aop操作的,Aspectl不是spring的一部分,而且是一个独立的AOP框架,一般把Aspectl和Spring一起使用,进行AOP操作,因为这样更方便
基于Aspectl进行AOP 操作的方式有两种
1.基于xml配置文件的方式实现
2.基于注解的方式实现
基于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"
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
http://www.springframework.org/schema/aop/spring-aop.xsd
">
<!-- 开启组件扫描,当前扫描com.zking下的全部组件-->
<!-- <context:component-scan base-package="com.zking"></context:component-scan>-->
<!-- 是 Spring 的注解驱动配置之一,用于自动扫描指定包下的组件,-->
<!-- 如带有 @Component, @Repository, @Service, @Controller 等注解的类,并将其注册为 Spring 的 Bean。-->
<context:component-scan base-package="com.zking"></context:component-scan>
<!-- <context:component-scan base-package="com.zking">-->
<!-- <context:exclude-filter type="assignable" expression="com.zking.aop.LogAspect"/>-->
<!-- </context:component-scan>-->
<!-- 开启aspectl生成代理对象-->
<!-- roxy-target-class="true"代理目标可以是类,默认为接口-->
<aop:aspectj-autoproxy/>
</beans>
首先我们在xml中开启组件扫描,扫描com.zking下面的所以类,并且开启aspectl生成代理对象
现在我有一个UserServerImp的类,类中有这些方法
package com.zking.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import javax.management.DescriptorKey;
//@Aspect : 表明当前类是增强类
@Aspect
@Order(1)
@Component
public class LogAspect {
// 设置切点
//@Pointcut("execution(* com.zking.dao..*.*(..))")
@Pointcut("execution(* com.zking..*.*(..))")
public void pointcut(){
}
@Before("pointcut()")
public void before() {
System.out.println("UserSeverimp前置通知");
}
@After("pointcut()")
public void after(){
System.out.println("UserSeverimp后置通知");
}
@AfterReturning("pointcut()")
public void afterReturning(){
System.out.println("UserSeverimp返回通知");
}
@AfterThrowing("pointcut()")
public void afterThrowing(){
System.out.println("UserSeverimp异常通知");
}
@Around("pointcut()")
public void around(ProceedingJoinPoint joinPoint) throws Throwable{
// System.out.println("UserSeverimp环绕通知");
joinPoint.proceed();
System.out.println("UserSeverimp环绕通知");
}
}
并且又创建了一个类,@Aspect : 表明当前类是增强类,
@Pointcut("execution(* com.zking..*.*(..))")
这个帮忙当前com.zking下面所以类中所以的方法,属性为任意类型,这样就组成了一个切面,切面下面有各种各样方方法,那么在执行程序时就会去扫描,看那些增强类,然后对类实现增强功能,
可以看到,本来我应该只是会输出用户添加的,但是因为我们配置了AOP,他就会在我们代码执行之前先执行我们增强类中的方法。好了,今天就到这里啦,累了兄弟们。