目录
这是基于黑马Spring的笔记
写再前面
开始
@Component(value=bean的名称)
componet衍生出的3个注解
Bean内部的属性进行注入
非自定义的Bean管理
使用配置类完全替代XML配置文件
配置类中的注解
spring中的其他注解(偶尔会用到)
Spring注解的解析原理
spring使用注解整合第三方框架mybatis
XML和@Mapperscan注解整合Mybatis的底层原理:
@import整合第三方框架
基于注解的AOP
写再前面
IOC和AOP都是面向切面编程(Aspect-Oriented Programming)的实现方法。
它们提供了一种抽象机制,可以实现在程序运行期动态地将额外功能插件到现有代码中。
IOC全称是Inversion of Control,即控制反转。它是Spring框架的核心,可以实现对象之间的依赖注入。基于IOC,对象的依赖关系由容器在运行期决定,完成对象的装配。这可以降低对象之间的耦合,使设计更加灵活。
AOP全称是Aspect-Oriented Programming,即面向切面编程。它可以实现在不修改源代码的情况下,为程序动态添加功能。在程序执行过程中,AOP可以在合适的连接点(如方法调用前/后),插入切面来修改程序逻辑。这有助于实现诸如日志、安全等通用功能,而不用在业务逻辑代码中夹杂各种非业务逻辑。
简单来说,IOC和AOP的区别在于:
- IOC 注重在对象创建与依赖注入,目的在于降低耦合、增强灵活性。它由容器在运行期决定对象依赖关系。
- AOP 注重在动态插入切面来影响程序运行逻辑,目的在于实现通用功能而不改变原有代码。它允许在程序运行期间插入切面来修改逻辑。所以,尽管IOC和AOP都属于面向切面编程范畴,但侧重点不同:
- IOC侧重对象的依赖关系注入,实现解耦和灵活设计。
- AOP侧重在运行期动态修改程序逻辑,实现通用功能的插入。两者都是实现高内聚低耦合的有效手段,广泛应用在企业级框架与项目中。Spring就是典型代表,它同时采用IOC和AOP实现解耦、通用功能和业务逻辑分离。
通过b站上的黑马的spring课程做的笔记。
Spring 3.0时代,进入全注解时代。S
pringboot就是一个几乎零配置的企业级开发框架。
开始
在不使用配置类时在applicationcontext配置文件中引入(后面也可以完全以注解的方式代替配置类)
<!-- 配置扫描组件所在的包 -->
<context:component-scan base-package="demo.pojo"/>
@Component(value=bean的名称)
若没有value则默认是类名的小写作为beanname.
测试用例
package demo.pojo;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
@Component(value = "yzh")
@Scope("singleton")
@Lazy(false)
public class user {
public user() {
System.out.println("无参构造执行");
}
@PostConstruct
void init()
{
System.out.println("实例化后执行");
}
@PreDestroy
void destroy()
{
System.out.println("bean销毁前执行");
}
}
componet衍生出的3个注解
既不属于web层,Service层,Dao层,普遍使用@Component.
Bean内部的属性进行注入
@Value 在bean内部属性注入时,可直接放在bean的属性上,也可以放在bean的set方法中.
@Autowired 首先会根据类型进行注入,若发现有多个类型匹配,则根据注入的属性名进行匹配,若依然匹配不成功则提示报错。(在这种情况下,可以结合@Autowired,根据名称进行注入)
@Resource 不指定名称时等同于@Autowired;指定名称时根据名称进行注入。
@Autowired扩展:
值得注意的是,当@AutoWired注解在方法上时,会根据参数的名字或者是集合中的类型进行注入(这种情况若发现有多个类型时,会全部注入到集合当中。)
非自定义的Bean管理
非自定义Bean不能像自定义Bean一样使用@Compnent进行管理,非自定义的Bean需要通过工厂的方式进行实例化,使用@Bean标注方法即可,@Bean的属性为beanName.若不加beanname则默认为方法名。(如以下的dataSource)。因为需要@Bean注解被扫描到,所以非自定义的bean所在的类必须被Spring容器管理。这也是为什么以下的'other'必须加component的原因。
见实例
package demo.otherbean;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import javax.sql.DataSource;
@Component
public class other {
@Bean("druid")
public DataSource dataSource()
{
DruidDataSource druid = new DruidDataSource();
return druid;
}
}
当非自定义的Bean需要参数注入时:
基本数据类型 :@Value (位置放在参数内部)
引用类型:
1.根据名称进行注入时:@Qualifier("参数名称")此处可以省略@Autowired(这点和自定义bean的@Qualifier结合@Autowired注入时不同)
2.根据类型进行注入时: 可不加任何注解。如下面的userservice.
使用配置类完全替代XML配置文件
通过以上的注解已经能够代替bean标签,在使用配置文件时通过ClassPathXmlApplicationContext类去加载我们的配置文件;
ClassPathXmlApplicationContext context=
new ClassPathXmlApplicationContext("applicationcontext.xml");
而使用配置类时,则用
AnnotationConfigApplicationContext 去加载配置类。
ApplicationContext context=
new AnnotationConfigApplicationContext(springconfig.class);
配置类中的注解
@ComponentScan相当于
<context:component-scan base-package="demo.otherbean"/>
@ComponentScan相当于
<context:property-placeholder location="classpath:test.properties"/>
若有多项扫描项或需要读取的配置文件,则用数组形式。参考以下实例
package demo.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
@Configuration //标明这是一个配置类 交给Spring容器去管理
@ComponentScan({"demo.pojo","demo.otherbean"})
@PropertySource({"test.properties"})
public class springconfig {
}
@import 用于加载其他的配置文件,用于替代原xml标签中的<import>
spring中的其他注解(偶尔会用到)
Spring注解的解析原理
XML方式的组件扫描:在Spring容器创建时,使用XML命名空间的形式扫描指定的包以及子包下的类,识别注解,创建Beandefinition 存储到beandefinitionMap中最终进入单例池。
注解方式如@ComponentScan的组件扫描:通过BeanFactoryProcessor(bean工厂处理器)扫描注解创建Beandefinition存储到beandefinitionMap中最终进入单例池。而Bean的属性注入则通过BeanPostProcessor(Bean后处理器)扫描@Autowired等注解进行属性的注入。
spring使用注解整合第三方框架mybatis
原来的XML方式整合:
步骤
- 需要创建SqlSessionFactoryBean,而SqlSessionFactoryBean需要注入一个引用类型为DataSource。
- 创建 DataSource。DataSource属于非自定义的bean,需要通过工厂方法的方式进行注入,且该工厂方法所在的类需要被Spring容器管理.
jdbc.properties 连接数据库的配置信息:
在这里发现了数据的格式问题,我在另一个项目中用yaml文件配置.在yaml文件里数据库的密码如上的061457需要加引号才能连接成功,而在.properties的配置文件中不能加引号才连接成功。(目前认为应该是配置文件的约束问题)
目录结构:
关于配置是否成功:
有个mybatisX的插件,一般来说xml文件能找到其对应的mapper接口映射文件则没什么问题。
核心注解
@MapperScan扫描Mapper接口
@PropertySource 加载配置文件
package demo.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import javax.sql.DataSource;
@Configuration //标明这是一个配置类 交给Spring容器去管理
@ComponentScan({"demo.pojo","demo.otherbean"})
@PropertySource({"jdbc.properties"})
@MapperScan("demo.mapper")
public class springconfig {
@Bean
public DataSource dataSource(
@Value("${jdbc.username}") String username,
@Value("${jdbc.drive}") String drive,
@Value("${jdbc.url}") String url,
@Value("${jdbc.pwd}") String pwd
)
{
System.out.println(username+":"+drive+":"+url+":"+pwd);
DruidDataSource druid = new DruidDataSource();
druid.setDriverClassName(drive);
druid.setUrl(url);
druid.setUsername(username);
druid.setPassword(pwd);
return druid;
}
@Bean
public SqlSessionFactoryBean SqlSessionFactoryBean(DataSource dataSource) //此处注入不加注解则通过类型进行注入
{
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
return sqlSessionFactoryBean;
}
}
XML和@Mapperscan注解整合Mybatis的底层原理:
简单的说:
都是扫描Mapper接口(比如以上的UserMapper),注册MapperFactorybean.而底层的MapperFactoryBean的getobject方法返回接口的实现。只不过入口不同,XML方式直接去配置MapperScannerConfig类,而注解通过导入MapperScannerRegister,通过MapperScannerRegister去注册MapperScannerConfig
@import整合第三方框架
Spring与Mybatis的注解方式整合有个重要的技术点就是@Import,第三方框架与Spring整合很多都是凭借自定义标签完成的,而第三方框架与Spring整合注解方式很多是靠@Import注解完成的。
比如在myatis的@MapperScan注解中
开发用的最多的还是导入普通的配置类。而需要整合自定义的第三方框架时,使用@Import导入实现ImportSelector接口的类以及实现ImportBeanDefinitionRegistrar接口的类就比较普遍了。
下面给出一个实例(自定义注解导入实现ImportBeanDefinitionRegistrar接口的类)
自定义注解
package demo.config;
import org.springframework.context.annotation.Import;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Import(MyImportBeanDefinitionRegistrar.class)
public @interface Mymapper {
}
基于注解的AOP
Spring框架的AOP支持两种代理方式:基于JDK动态代理和基于CGLIB的代码生成代理。
基于JDK动态代理:使用JDK动态代理进行AOP代理时,目标对象必须实现至少一个接口。Spring会为实现了指定接口的目标对象动态生成一个代理对象,并在代理对象中拦截对目标方法的调用,并执行相应的增强逻辑。由于JDK动态代理是基于接口进行代理的,因此对于没有实现任何接口的目标类,无法使用JDK动态代理进行代理。
基于CGLIB的代码生成代理:如果目标对象没有实现任何接口,则Spring将使用CGLIB库来为该对象创建一个子类,并将代理逻辑织入到子类中。与JDK动态代理不同,CGLIB代理是通过生成目标类的子类来实现的,因此它能够代理没有实现任何接口的类。但是,由于CGLIB是通过生成目标类的子类来实现代理,所以对于final方法、private方法等无法覆盖的方法,也无法使用CGLIB来进行代理。
总之,Spring框架的AOP支持多种代理方式,在选择代理方式时需要根据目标对象是否实现接口、要代理的方法类型(final方法、private方法等)以及代理性能等因素来进行选择。通常情况下,如果目标对象实现了接口,则优先使用基于JDK动态代理的方式;否则,使用基于CGLIB的代码生成代理。
开启自动代理的注解,默认为基于JDK的动态代理,一般在配置类加上这个注解(因为基于注解开发,肯定要创建一个配置类)我发现无论有没有实现接口的类以上面两种方式(JDK的动态代理,CJLIB)都能实现AOP.
@EnableAspectJAutoProxy
基于CJlib的代理
@EnableAspectJAutoProxy(proxyTargetClass=true)
AOP的相关Maven依赖
<!--提供AOP基本模块的功能-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.3.20</version>
</dependency>
<!-- AspectJ 是一种面向切面编程(AOP)框架,aspectjweaver 提供了实现基于 AspectJ 的 AOP 功能所需的运行时依赖关系。-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>
<!-- spring-aspects 为 Spring AOP 提供了额外的方面(Aspect),可以用于事务管理等功能。-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.16</version>
</dependency>
通知类
package demo.advise;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;
@EnableAspectJAutoProxy
@Component
@Aspect
public class advise {
@Pointcut("execution( * demo.otherbean.*.*(..))")
public void myponitcut(){} //切点表达式的抽取
@Before("myponitcut()")//切点表达式,demo包下的otherbean包下的所有类的所有方法的所有参数为切点
public void beforeAdvise(JoinPoint joinPoint)
{
System.out.println("当前目标对象是:"+joinPoint.getTarget());
System.out.println("表达式:"+joinPoint.getStaticPart());
System.out.println("前置通知");
}
@After("myponitcut()")//切点表达式,demo包下的otherbean包下的所有类的所有方法的所有参数为切点
public void afterAdvise(JoinPoint joinPoint)
{
System.out.println("当前目标对象是:"+joinPoint.getTarget());
System.out.println("表达式:"+joinPoint.getStaticPart());
System.out.println("后置通知");
}
}
通过这个通知类可以看到:
它可以带参数为JoinPoint(切点),更可以通过其getTarget方法拿到切点所在的类对象。