概述
spring是javaee轻量级开源框架;
spring有两个最核心的模块: IOC和AOP;
IOC:(inverse of control) ,控制反转,将创建对象的过程交给spring进行管理;
AOP:(Aspect Oriented Programming),面向切面编程。aop用来封装多个类的公共行为。
入门程序
//引入spring-context依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.26</version>
</dependency>
//创建一个user类
package com.zhang;
public class user {
public void add(){
System.out.println("this is a add method");
}
}
//配置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">
<!--
id:唯一标识
class:要创建对象所在类的全路径
-->
<bean id="user" class="com.zhang.user"></bean>
</beans>
//通过读取配置文件创建user对象
package org.example;
import com.zhang.user;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
user user = (user)context.getBean("user");
System.out.println(user);
user.add();
}
}
如何使用反射创建对象?
1.加载bean.xml配置文件
2.对xml文件进行解析
3.获取xml文件bean标签属性值
4.使用反射根据类的全路径创建对象
Class clazz = class.forName("com.Xxx.xxx");
(User) user = (User)clazz.getDeclaredConstructor().newInstance();
创建的对象放到哪里?
Map<String,BeanDefinition> beanDefinitionMap
其中key:唯一标识,value:类的定义(描述信息)
Apache Log4j2
开源的日志记录组件;
log4j2组成:
- 日志信息的优先级
- 日志信息的输出目的地
- 日志信息的输出格式
<--依赖--> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.13.3</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-slf4j-impl</artifactId> <version>2.13.3</version> </dependency>
<?xml version="1.0" encoding="UTF-8"?> <!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL --> <!--Configuration后面的status,这个用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,你会看到log4j2内部各种详细输出--> <!--monitorInterval:Log4j能够自动检测修改配置 文件和重新配置本身,设置间隔秒数--> <configuration status="WARN" monitorInterval="30"> <!--先定义所有的appender--> <appenders> <!--这个输出控制台的配置--> <console name="Console" target="SYSTEM_OUT"> <!--输出日志的格式--> <PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/> </console> <!--文件会打印出所有信息,这个log每次运行程序会自动清空,由append属性决定,这个也挺有用的,适合临时测试用--> <File name="log" fileName="log/test.log" append="false"> <PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/> </File> <!-- 这个会打印出所有的info及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档--> <RollingFile name="RollingFileInfo" fileName="${sys:user.home}/logs/info.log" filePattern="${sys:user.home}/logs/$${date:yyyy-MM}/info-%d{yyyy-MM-dd}-%i.log"> <!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)--> <ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/> <PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/> <Policies> <TimeBasedTriggeringPolicy/> <SizeBasedTriggeringPolicy size="100 MB"/> </Policies> </RollingFile> <RollingFile name="RollingFileWarn" fileName="${sys:user.home}/logs/warn.log" filePattern="${sys:user.home}/logs/$${date:yyyy-MM}/warn-%d{yyyy-MM-dd}-%i.log"> <ThresholdFilter level="warn" onMatch="ACCEPT" onMismatch="DENY"/> <PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/> <Policies> <TimeBasedTriggeringPolicy/> <SizeBasedTriggeringPolicy size="100 MB"/> </Policies> <!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件,这里设置了20 --> <DefaultRolloverStrategy max="20"/> </RollingFile> <RollingFile name="RollingFileError" fileName="${sys:user.home}/logs/error.log" filePattern="${sys:user.home}/logs/$${date:yyyy-MM}/error-%d{yyyy-MM-dd}-%i.log"> <ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/> <PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/> <Policies> <TimeBasedTriggeringPolicy/> <SizeBasedTriggeringPolicy size="100 MB"/> </Policies> </RollingFile> </appenders> <!--然后定义logger,只有定义了logger并引入的appender,appender才会生效--> <loggers> <!--过滤掉spring和mybatis的一些无用的DEBUG信息--> <logger name="org.springframework" level="INFO"></logger> <logger name="org.mybatis" level="INFO"></logger> <root level="all"> <appender-ref ref="Console"/> <appender-ref ref="RollingFileInfo"/> <appender-ref ref="RollingFileWarn"/> <appender-ref ref="RollingFileError"/> </root> </loggers> </configuration>
容器IOC
IOC(Inversion of Control):控制反转,是一种思想;Spring通过IOC容器来管理 所有Java对象的实例化和初始化,控制对象与对象间的依赖关系; 将IOC容器管理的Java对象称为Spring bean;
Spring创建IOC容器的方式:Beanfactory(内部开发人员用) ApplicationContext(子接口);
依赖注入DI
DI(Dependency injection):依赖注入,依赖注入实现了控制反转思想。
依赖注入指spring创建对象的过程中,将对象依赖属性通过配置进行注入;
依赖注入常见的两种方式:set注入,构造注入;
基于xml管理bean
1.获取bean
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
方式一:根据id获取
User user = (User)context.getBean("bean_id");
方式二:根据类型获取
//根据类型获取,要求IOC容器中指定类型的bean只能有一个 User user = context.getBean(User.class);
方式三:根据id和类型获取
User user = context.getBean("user",User.class);
注意:
如果组件类实现了接口并且bean唯一,可以根据接口类型获取bean;
如果一个接口有多个实现类,这些实现类都配置了bean,不可以根据接口类型获取bean;
2.依赖注入
1.setter注入
<?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"> <bean id="user" class="com.zhang.user"> <property name="name" value="zhangsan"></property> </bean> </beans> <-- 使用property 属性 name是属性名 value是要赋的值 -->
2.构造器注入
<bean id="Xxx" class = "Xxx"> <constructor-arg name="Xxx" value="Xxx"></constructor-arg> </bean>
3.特殊值处理
<--null--> <property name="abc"> </null> </property> <--xml实体--> <property name="abc" value" <>"></property> <--xml解析看到cdata节就知道里面的内容是纯文本,不会当作xml标签或属性来处理--> <property name="abc"> <value> <![CDATA[ <> ]> </value> </property>
4.为对象类型属性赋值
<-- a类中的属性b的数据类型是引用数据类型,用如下方法为属性b赋值 方式一:外部bean --> <property name = "属性b" ref = "b的bean_id"></property> <--方式二:内部bean--> <property name =""> <bean id = "" class=""> ... </bean> </property> <--级联赋值-->
5.为数组类型属性赋值
<bean id ="" class=""> <property name=""> <array> <value></value> ... <value></value> </array> </property> </bean>
6.集合类型属性赋值
<!--List类型属性--> <bean id = "" class=""> <property name=""> <list> <ref bean=""></ref> ... <ref bean=""></ref> </list> </property> </bean> <!--Map类型属性--> <bean id = "" class=""> <property name=""> <map> <entry> <key> <value>xxx</value> </key> <ref bean="bean_id"></ref> </entry> </map> </property> </bean> <!--引入集合bean--> <util:list id=""> <ref bean=""></ref> <ref bean=""></ref> </util:list> <util:map id=""> <entry> <key> <value>xxx</value> </key> <ref bean="bean_id"></ref> </entry> </util:map>
6.p命名空间
配置
<
beans xmlns:p="http://www.springframework.org/schema/p"
...
>
<bean id="" calss="" p:xxx="" p:xxx=""></bean>
<beans>
7.引入外部属性文件
//配置文件中 添加xmlns:context相关 配置 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" 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/context http://www.springframework.org/schema/context/spring-context.xsd"> <--加载bean- out.properties--> <context:property-placeholder location="bean-out.properties"></context:property-placeholder> <bean id="user" class="com.zhang.user"> <--通过${} 获取值--> <property name="name" value="${s.name}"></property> </bean> </beans>
8.bean的作用域
在Spring中通过配置bean标签的scope属性来指定bean的作用域范围
取值 含义 创建对象的时机 singleton(默认) 在ioc容器中,这个bean的对象始终为单例 ioc容器初始化时 prototype 这个bean在ioc容器中有多个实例 获取bean时
9.bean生命周期
- bean对象创建(调用无参构造器)
- 给bean对象设置相关属性
- bean后置处理器(初始化前)
- bean对象初始化
- bean后置处理器(初始化后)
- bean对象创建完成
- bean对象销毁
- IoC容器关闭
10.FactoryBean
整合第三方框架
......
实现FactoryBean接口
11.基于xml自动装配
<bean id="" class="" autowire="byType"></bean>
byType:根据类型匹配IOC容器中的某个兼容类型的bean,为其自动赋值;
<bean id="" class="" autowired="byName"></bean>
byName:
基于注解管理bean
1.开启组件扫描
xml方式
<context:component-scan base-package="包名"></comtext:component-scan>
注解方式
2.使用注解定义bean
注解 说明 @Component 用于描述Spring中的bean @Repository 用于将数据访问层的类标识为bean @Service 用于将业务层的类标识为bean @Controller 用于将控制层的类标识为bean
3.注入
@Autowired注入
//1.属性注入 @Autowired private User user; //2.set方法注入 private User user; @Autowired public void setUser(User user){ ... } //3.构造方法注入 private User user; @Autowired public User(){ ... } //4.形参注入 private User user; public User(@Autowired Name name){ ... } //5.只有一个有参数构造函数,@Autowired可以省略 //6.@Autowired和@Qualifier联合使用,指定bean名称
@Resource注入
@Resource注解是jdk扩展包中的。更加具有通用性。
会默认根据byName,未指定name时,使用属性名作为name。通过name找不到会通过byType装配。
使用位置:属性,setter方法
4.配置类替代配置文件
package com.zhang.config; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @Configuration @ComponentScan("com.zhang") public class config { }
手写IoC
创建两个注解(创建对象,属性注入);
创建bean容器接口ApplicationContext定义方法,返回对象;
实现bean容器接口(返回对象,根据包规则加载bean);
需要自定义两个注解,用来标识 创建对象Bean和依赖注入DI
package com.zhang.bean;
import com.zhang.anno.Bean;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
public class AnnotationApplicationContext implements ApplicationContext{
//创建map集合,存放bean对象
private static Map<Class,Object> beanFactory = new HashMap<Class,Object>();
//
private static String rootPath;
//返回对象
@Override
public Object getBean(Class clazz) {
return null;
}
//创建有参构造器,传递包路径,设置包扫描规则
public AnnotationApplicationContext(String basePackage){
//1.把包名中的.替换成/
String packagePath = basePackage.replaceAll("\\.", "\\\\");
//2.获取包绝对路径
try {
Enumeration<URL> urls = Thread.currentThread().getContextClassLoader().getResources(packagePath);
while(urls.hasMoreElements()){
URL url = urls.nextElement();
String filepath = URLDecoder.decode(url.getFile(), "utf-8");
rootPath = filepath.substring(0, filepath.length() - packagePath.length());
//包扫描
try {
loadBean(new File(rootPath));
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private static void loadBean(File file) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
//1.判断是否为文件夹
if(file.isDirectory()){
//2.获取文件夹里所有内容
File[] childFiles = file.listFiles();
//3.判断文件夹为空,直接返回
if(childFiles == null || childFiles.length == 0){
return;
}
//4.不为空遍历文件夹所有内容
for(File child : childFiles){
//4.1 遍历每个File对象,如果是文件夹递归
if(child.isDirectory()){
loadBean(child);
}else
//4.2 是文件
{
//4.3 得到包路径+类名称部分
String pathWithClass = child.getAbsolutePath().substring(rootPath.length() - 1);
//4.4 判断当前文件类型是否为 .class
if(pathWithClass.contains("class")){
//4.5 如果是.class,把路径\替换成. 把.class去掉
String allName = pathWithClass.replaceAll("\\\\", "\\.")
.replace(".class", "");
//4.6 判断类上是否有注解@Bean
//4.6.1 获取类的class对象
Class<?> aClass = Class.forName(allName);
//4.6.2 是否为接口
if(aClass.isInterface()){
return;
}
Bean annotation = aClass.getAnnotation(Bean.class);
if(annotation != null){
//4.7 把对象实例化之后,放到map集合中
Object instance = aClass.getConstructor().newInstance();
//判断当前类是否有接口
if(aClass.getInterfaces().length > 0){
beanFactory.put(aClass.getInterfaces()[0],instance);
}else{
beanFactory.put(aClass,instance);
}
}
}
}
}
}
}
}
面向切面AOP
代理模式
静态代理
动态代理(aop底层)
AOP概述
AOP(Aspect Oriented Programming)是一种设计思想。通过预编译方式和运行期动态代理方式实现,在不修改源代码的情况下,给程序动态统一添加额外功能的一种技术。
动态代理分类
有接口,使用jdk动态代理,生成接口实现类代理对象
没有接口,使用cglib动态代理,生成子类代理对象
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.3.26</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>6.0.2</version>
</dependency>
</dependencies>
基于注解的aop
通知类型
@Before
@AfterReturning
@AfterThrowing
@After
@Around
基于xml的aop
未完待续 57中间/88 // 2024年3月26日