欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 房产 > 建筑 > Spring01

Spring01

2024/10/25 14:34:53 来源:https://blog.csdn.net/y123916/article/details/142283879  浏览:    关键词:Spring01

spring框架

spring是轻量级的容器框架

spring framework

1、Spring核心学习内容 IOC、AOp, jdbcTemplate,声明式事务
2、IOC:控制反转,孚以管理部8号对象
3.AOP:切面编程4.JDBCTemplate:是spring提供一套访问数据库的技术,应用性强,相对好理解5.声明式事务:基于ioc/aop实现事务管理,理解有需要小伙伴花时间6.IOC, AOP 是重点同时难点

重要概念

1.Spring可以整合其他的框架(老韩解读: Spring是管理框架的框架)

2.Spring有两个核心的概念: IOC 和 AOP
3.IOC [Inversion Of Control反转控制]。传统的开发模式[JdbcUtils/反射]程序---->环境//程序读取环境配置,然后自己创建对象

1.spring根据配置文件xml注解,创建对象,并放入容器中,并且可以完成对象之间的依赖.

spring可以整合其他框架,是管理其他框架的框架

spring中有两个核心概念:ioc控制反转 aop 切面编程文件

在传统开发中:程序读取环境配置信息{可能会用到new,或者反射}然后自己创建对象

在ioc开发模式:容器中创建好对象---->被程序使用

容器创建对象的方法:1.xml配置文件,2.注解

1.spring根据配置文件或者配置文件创建对象,并放入到容器中;并完成对象之间的依赖

2.当需要某个对象实例的时候,可以直接从容器中获取

3.编写人员可以更加关注对象完成相应的业务

4.DI{dependency injection}依赖注入

5.Spring最大的价值,通过配置提供给程序使用

实例

package com.Test;import com.bean.Monster;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;/*** @Author: zs* @Description: TODO* @DateTime: 2024/9/9 13:56**/
public class MonsterTest {@Testpublic void getMonster() {//创建容器ApplicationContext//该容器和容器配置文件关联//建议用接口接收,ApplicationContext ioc =new ClassPathXmlApplicationContext("beans.xml");//拿到容器是为拿到容器关联配置文件中的Java对象//可以通过getbean获取对应的对象,通过id,默认返回的类型是object,运行类型是monster/*这里的运行类型可以改为monter,因为getclass方法获取到该对象的运行类型为monterObject monster01 = ioc.getBean("monster01");*/Monster monster01 = (Monster) ioc.getBean("monster01");//完成向下转型之后可以分别获取属性//输出System.out.println("monster01="+ monster01 + monster01.getClass());System.out.println(monster01.getName()+monster01.getSkill()+monster01.getMonsterid());System.out.println("ok");//可以在获取的时候直接指定class类型Monster monster2 = ioc.getBean("monster01", Monster.class);System.out.println(monster2.getName()+monster2.getSkill()+monster2.getMonsterid());}
}

配置文件编写

<?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">
<!--    配置monster对象Javabean-->
<!--    在beans中可以配置多个bean--><!--bean表示一个对象-->
<!--    class创建Java对象类的全路径-->
<!--    Spring 底层使用反射创建的-->
<!--    id表示Java对象在spring容器中的id,通过id可获得对象 id是唯一的-->
<!--    <property name="name" value="牛哈"/>用于给对象属性赋值,如果不改就是默认值--><bean class="com.bean.Monster" id="monster01"><property name="monsterid" value="100"/><property name="name" value="牛哈"/><property name="skill" value="叫"/></bean>
<!--    完成Java对象的创建--></beans>
package com.bean;/*** @Author: zs* @Description: TODO* @DateTime: 2024/9/9 13:26**/
public class Monster {private Integer monsterid;private String name;private String skill;public Monster(Integer monsterid, String name, String skill) {this.monsterid = monsterid;this.name = name;this.skill = skill;}public Monster() {}
//无参构造器一定要写,因为反射要用public Integer getMonsterid() {return monsterid;}public void setMonsterid(Integer monsterid) {this.monsterid = monsterid;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getSkill() {return skill;}public void setSkill(String skill) {this.skill = skill;}}

classpathxmlapplicationcontext 为什么可以读取到beans.xml

 public void classPath() {File file = new File(this.getClass().getResource("/").getPath());System.out.println(file);//结果 /*D:\javaprogram\Spring\out\production\Spring*///读取路径是out目录}

debug时会发现:beandefinitionmap会保存类的信息,但是不创建对象

当需要对象时,通过反射创建对象时用;

IOC容器

底层机制

当代码执行到

  ApplicationContext ioc =new ClassPathXmlApplicationContext("beans.xml");

时,容器已经创建完毕

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在beanfactory下的beandefinitionmap中就会保存配置文件中的信息,通过集合的方式 大小512

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图一

ConcurrentHashMap类型时集合,存bean节点配置信息

在beanDefinitionMap中有属性table

table为数组 类型为ConcurrentHashMap$node

因为是数组类型,可以存放很多bean对象信息,就是bean.xml配置文件

初始化为512, 会自动扩容

图二

通过hash算法monster01对象信息就保存在index = 217 位置

保存是以 ConcurrentHashMap$node类型

key值就是beans.xml文件中的id值

value 就是monster01对象的[属性/属性值/是否为懒加载]

属性值保存在propertyValueList,就是记录monster01对象的属性值

beandefinitionmap只保存了类信息,并不保存对象
在这里插入图片描述
在这里插入图片描述

类的对象用concurrenthashmap$node的类型保存,类型为基于泛型的,

因为保存类的对象时,会默认为单例所以存在这个位置

在beanfactory中的另一个属性singletonObjects

类型为concurrentHashMap

其中有个属性table 类型是concurrenthashmap$node

如果在beans.xml文件中配置的对象时单例的 就是初始化放在里边

当我们getbean(“id”)时,首先会在底层查beandifinitionmap中按照id 名字查询是为单例,如果为单例会在singletonobject[类似单例池的作用]查找并返回,若不是单例会通过动态反射机制创建一个对象放进去.
在这里插入图片描述

为了方便我们快速查找定位,直接存入id[就是在beans.xml文件中配置的bean的名称]值,

如果先查看自己定义了多少对象,通过beandefinitionnames方法查找

        //查看容器注入了那些bean对象  输出bean的idString[] beanDefinitionNames = ioc.getBeanDefinitionNames();for(String beanDefinitionName : beanDefinitionNames) {System.out.println(beanDefinitionName);}

原理模拟

手动开发一个简单的spring基于XML配置的程序

第一步引入jar包{dom4j}

xml文件用的是上边的deans.xml

类的代码

package com.applicationcontext;import com.bean.Monster;
import com.sun.org.apache.bcel.internal.generic.NEW;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;import java.io.File;
import java.lang.reflect.Method;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;/*** @Author: zs* @Description: 实现spring的一个简单容器* 实现bean.xml文件的解析,并生成容器* @DateTime: 2024/9/10 12:09**/
public class ApplicationContext {private ConcurrentHashMap<String, Object> singletonobject =new ConcurrentHashMap<>();//构造器//接收一个容器的配置文件,该文件默认在srcpublic ApplicationContext(String iocBeanXmlFile) throws DocumentException, ClassNotFoundException, InstantiationException, IllegalAccessException {//1, 得到类加载路径"String path = this.getClass().getResource("/").getPath();//        创建saxreader,它用于读取 XML 文件并将其转换为 Java 对象。
//        这个类是基于 SAX (Simple API for XML) 解析器的,
//        它允许你以流的方式读取 XML 文件,而不需要将整个文件加载到内存中,SAXReader saxReader = new SAXReader();//得到document对象Document document = saxReader.read(new File(path + iocBeanXmlFile));//得到rootdocumentElement root = document.getRootElement();//获取bean元素Element bean = (Element) root.elements("bean").get(0);//获取id,属性,属性值,类的全路径String id = bean.attributeValue("id");String classPath = bean.attributeValue("class");List<Element> property = bean.elements("property");;//遍历Integer monsterId =Integer.parseInt(property.get(0).attributeValue("value"));String name = property.get(1).attributeValue("value");String skill = property.get(2).attributeValue("value");//        使用反射创建对象Class<?> aclass = Class.forName(classPath);//这里o对象就是monster,Monster o =(Monster) aclass.newInstance();//给对象转化类型,赋值//1.反射赋值
//        Method[] declaredMethods = aclass.getDeclaredMethods();
//        for (Method declaredMethod : declaredMethods ) {
//            declaredMethod.invoke();
//        }//2.直接赋值o.setMonsterid(monsterId);o.setName(name);o.setSkill(skill);//将创建的对象放入容器中singletonobject.put(id, o);}public Object getBean(String id) {//获取容器中的对象return singletonobject.get(id);}}

测试类的编写

package com.applicationcontext;import com.bean.Monster;
import org.dom4j.DocumentException;
import org.springframework.context.support.ClassPathXmlApplicationContext;/*** @Author: zs* @Description: TODO* @DateTime: 2024/9/10 12:18**/
public class ApplicationContextTest {public static void main(String[] args) throws DocumentException, ClassNotFoundException, InstantiationException, IllegalAccessException {ApplicationContext ioc = new ApplicationContext("beans.xml");Monster monster01 = (Monster)ioc.getBean("monster01");System.out.println(monster01);System.out.println(monster01.getClass());System.out.println(monster01.getName());System.out.println("ok");}
}

Spring管理bean-IOC

bean的配置方式1.基于xml文件配置的方式

​ 2.基于注解

bean的管理:1.创建bean对象

​ 2.注入属性

基于xml形式

1.通过类型获取

    <!--    配置monster 通过类型获取--><bean class="com.bean.Monster"><!--1.当我们给某个bean对象设置属性时底层使用setter方法完成的--><property name="monsterid" value="100"/><property name="name" value="牛哈"/><property name="skill" value="叫"/></bean>
        ApplicationContext ioc =new ClassPathXmlApplicationContext("beans.xml");//直接传入class类型Monster bean = ioc.getBean(Monster.class);System.out.println("bean" + bean);}

要求类型获取bean,要求容器IOC中的同一个类的bean只能有一个,否则会抛出异常NouUniqueBeanDefinitionException

应用场景:在一个线程中只需要一个对象实例的情况(单例)

2.通过构造器配置bean

<!-- 配置monster对象,通过构造器--><bean id="monster03" class="com.bean.Monster">
<!--        index索引表示构造器的第几个参数 从零开始计算-->
<!--        除了index 还可以通过name / type 来指定 类的构造器,不能有完全相同类型顺序的构造器
--><constructor-arg value="200" index="0"/><constructor-arg value="猴"  index="1"/><constructor-arg value="变"  index="2"/></bean>
   @Testpublic void setBeanByConstructor(){ApplicationContext ioc =new ClassPathXmlApplicationContext("beans.xml");Monster monster03 = ioc.getBean("monster03",Monster.class);System.out.println("monster03=" + monster03);}

通过P名称空间配置

//通过p名称空间来设置属性
@Test
public void setBeanByCP(){ApplicationContext ioc =new ClassPathXmlApplicationContext("beans.xml");Monster monster06 = ioc.getBean("monster06", Monster.class);System.out.println(monster06);
}
<!--    通过P名称空间配置-->
<!--    在编写代码时.p会报错,p空间没绑定-->
<!--    解决方法:光标放在爆红的地方,按alt+int 有时需要多试几次--><bean id="monster06" class="com.bean.Monster"p:monsterid="500"p:name="红孩儿"p:skill="玩火"/>
xmlns:p="http://www.springframework.org/schema/p"

引用其他bean

测试文件

    @Testpublic void setBeanByRef(){ApplicationContext ioc =new ClassPathXmlApplicationContext("beans.xml");MemberServiceImpl memberService = ioc.getBean("memberService", MemberServiceImpl.class);memberService.add();}

DaoImpl

public class MenberDAOImpl {//构造器.....public MenberDAOImpl() {System.out.println("MenberDAOImpl 构造器");}
//    完成添加public void add(){System.out.println("MemberDaoImpl add()方法");}
}

ServiceImpl

public class MemberServiceImpl {private MenberDAOImpl menberDao;public MenberDAOImpl getMenberDao() {return menberDao;}public void setMenberDao(MenberDAOImpl menberDao) {this.menberDao = menberDao;}public void  add(){System.out.println("service方法被调用");menberDao.add();}
}

配置文件

<!--    配置memberdaoimpl对象--><bean class="com.dao.MenberDAOImpl" id="memberDAO"/>
<!--    配置menmberserviceimpl对象-->
<!--    ref="memdao" 表示 memberserviceimpl对象的属性memberDao引用的对象是id = memberDao的对象-->
<!--    体现出spring容器的依赖注入--><bean class="com.service.MemberServiceImpl" id="memberService"><property name="menberDao" ref="memberDAO"/></bean>

引用内部bean对象

<!--    配置memberserviceimpl-使用内部bean--><bean class="com.service.MemberServiceImpl" id="memberService2"><property name="memberDao" ><bean class="com.dao.MemberDAOImpl"/></property></bean>

引用集合/数组类型的值赋值

list

<!--    配置master对象--><bean class="com.bean.Master" id="master"><property name="name" value="老头"/>
<!--        给list属性赋值--><property name="monsterList" ><list><ref bean="monster06"/><ref bean="monster03"/></list></property></bean>
<!--    配置master对象--><bean class="com.bean.Master" id="master"><property name="name" value="老头"/>
<!--        给list属性赋值--><property name="monsterList" ><list>
<!--                引用的方式--><ref bean="monster06"/><ref bean="monster03"/>
<!--                内部--><bean class="com.bean.Monster"><property name="name" value="老鼠"/><property name="monsterid" value="100"/><property name="skill" value="toushi"/></bean></list></property>
<!--        给map属性赋值--><property name="monsterMap"><map><entry><key><value>monster03</value></key><ref bean="monster03"/></entry><entry><key><value>monster06</value></key><ref bean="monster06"/></entry></map></property><property name="monsterSet"><set><ref bean="monster03"/><ref bean="monster06"/><bean class="com.bean.Monster"><property name="name" value="男"/><property name="monsterid" value="1000"/><property name="skill" value="吃"/></bean></set></property>
<!--        array标签中的使用value 还是bean,ref 需要根据实际情况--><property name="monsterName"><array><value>怪</value><value>女</value></array></property><property name="pros"><props><prop key="username">root</prop><prop key="password">123456</prop><prop key="ip">127.0.0.1</prop></props></property></bean>

使用util名称空间创建list

<!--    配置bookstore对象--><bean class="com.bean.BookStore" id="bookStore"><property name="bookList" ref="myBookList"/>
<!--            <list>-->
<!--                <value>三国演义</value>-->
<!--                <value>水浒传</value>-->
<!--                <value>你好</value>-->
<!--            </list>-->
<!--        </property>--></bean>
<!--    定义一个utillist 并指定一个id 可以达到复用的效果--><util:list id="myBookList"><value>三国演义2</value><value>水浒传2</value><value>你好2</value></util:list>
xmlns:util="http://www.springframework.org/schema/util"

级联属性赋值

在编程中,级联属性赋值通常指的是在对象之间设置属性值时,将一个对象的属性值赋给另一个对象的对应属性。这种操作在面向对象编程中很常见,尤其是在处理具有复杂关系的对象时。级联赋值可以简化代码,避免重复编写相同的赋值语句。

以Java为例,假设我们有两个类 PersonAddress,其中 Person 类包含一个 Address 类型的属性:

public class Address {private String street;private String city;private String zipCode;// 构造器、getter和setter省略
}public class Person {private String name;private Address address;// 构造器、getter和setter省略
}

如果我们想要级联地为 Person 对象的 address 属性赋值,可以这样做:

public class Main {public static void main(String[] args) {Address address = new Address();address.setStreet("123 Main St");address.setCity("Anytown");address.setZipCode("12345");Person person = new Person();person.setName("John Doe");person.setAddress(address); // 级联赋值// 现在person对象有了一个包含地址信息的address属性}
}

emp类创建

package com.bean;/*** @Author: zs* @Description: TODO* @DateTime: 2024/9/10 17:01**/
public class Emp
{private String name;private Dept dept;public Emp() {}public String getName() {return name;}public void setName(String name) {this.name = name;}public Dept getDept() {return dept;}public void setDept(Dept dept) {this.dept = dept;}@Overridepublic String toString() {return "Emp{" +"name='" + name + '\'' +", dept='" + dept + '\'' +'}';}
}

dept类创建

package com.bean;/*** @Author: zs* @Description: TODO* @DateTime: 2024/9/10 17:00**/
public class Dept {private String name;public Dept() {}public String getName() {return name;}public void setName(String name) {this.name = name;}@Overridepublic String toString() {return "Dept{" +"name='" + name + '\'' +'}';}
}

级联配置文件

<!--    配置dept--><bean class="com.bean.Dept" id="dept"/>
<!--    配置emp--><bean class="com.bean.Emp" id="emp"><property name="name" value="jj"/><property name="dept" ref="dept"/>
<!--        这我希望给dept的name属性赋值--><property name="dept.name" value="开发部门"/></bean>

通过静态工厂获取对象

创建一个静态工厂类

package com.factory;import com.bean.Monster;import java.util.HashMap;
import java.util.Map;/*** @Author: zs* @Description: TODO* @DateTime: 2024/9/10 17:22**/
public class MyStaticFactory {private static Map<String, Monster> monsterMap;//使用static代码块进行初始化//static代码块特点:随着类的加载而加载,并且只加载一次static {monsterMap = new HashMap<>();monsterMap.put("monster01", new Monster(1000,"你啦","跑"));monsterMap.put("monster02",new Monster(100000,"你","走走走"));}//提供一个方法,返回monster对象public static Monster getMonster(String key) {return monsterMap.get(key);}
}

配置文件

<!--    配置一个monster对象-->
<!--    通过静态工厂类获取/配置bean-->
<!--    class 是静态工厂的全路径-->
<!--    foctory-mehtod 表示是静态工厂用哪个方法返回对象-->
<!--    constructor-arg value 返回静态工厂的那个对象--><bean id="my_monster01" class="com.factory.MyStaticFactory"factory-method="getMonster"><constructor-arg value="monster02"/></bean>

不管获取多少次都是通一个对象

动态实例工厂

<!--    配置monster对象--><bean id="myInstanceFactory" class="com.factory.MyInstanceFactory"/><bean id="my_monster02" factory-bean="myInstanceFactory" factory-method="getMonster"><constructor-arg value="monster03"/></bean>

通过factory bean

package com.factory;import com.bean.Monster;
import org.springframework.beans.factory.FactoryBean;import java.util.HashMap;
import java.util.Map;/*** @Author: zs* @Description: TODO* @DateTime: 2024/9/11 12:55**/
public class MyFactoryBean implements FactoryBean<Monster> {private String key;//就是配置时,指定获取的对象private Map<String, Monster> monsterMap;{monsterMap = new HashMap<String, Monster>();monsterMap.put("monster05", new Monster(500,"反复","请求"));monsterMap.put("monster06", new Monster(600,"ff","qq"));}public void setKey(String key) {this.key = key;}public Monster getObject() throws Exception {return monsterMap.get(key);}public Class<?> getObjectType() {return Monster.class;}public boolean isSingleton() {return true;}
}
<!--    配置factorybean--><bean id="my_monster05" class="com.factory.MyFactoryBean"><property name="key" value="monster05"/></bean>

bean的重用

<bean id="monster11" class="com.hspedu.spring.beans.Monster" parent="monster10"/>

或者

使用abstract = true 抽象用来继承

但是设置之后本类不能被实例化

bean创建顺序

默认是按照编写的顺序

当有依赖对象时,会先创建被依赖的对象

ioc是一个整体,先创建对象最后完成依赖关系 除depende之外

bean的单例和多实例

在spring容器中,默认按照单例创建的,即配置一个一个bean对象后,ioc容器只会创建一个bean实例

多例设置

添加标签
scope = "prototype"

设置多实例时,实例只会在执行getbean时创建

单例模式默认是lazy-init= false;不是懒加载,所以容器一加载对象就会创建

bean的生命周期

bean对象的创建是由jvm完成的

1,执行构造器

2,执行set方法

3,调用bean的初始化方法

4,使用bean

5,容器关闭时,调用销毁方法

bean的后置处理器

这个对象会在bean初始化方法调用之前和调用之后调用

功能可以操作整个bean文件的对象,就像一个切面

在Spring框架中,Bean后置处理器(Bean Post Processor)是一种特殊的组件,它允许你在Spring容器初始化任何bean之后,但在bean的属性被设置之后进行额外的处理。Bean后置处理器可以用来修改bean的定义、属性值,或者根据特定的逻辑决定是否要返回一个代理而不是原始bean实例。

关键特点:

1.实现接口:Bean后置处理器需要实现 org.springframework.beans.factory.config.BeanPostProcessor 接口。

两个主要方法

  • postProcessBeforeInitialization(Object bean, String beanName):在bean的初始化方法(如@PostConstruct注解的方法或InitializingBean接口的afterPropertiesSet方法)之前调用。
  • postProcessAfterInitialization(Object bean, String beanName):在bean的初始化方法之后调用。

3.作用时机:Bean后置处理器在Spring容器的bean生命周期的特定点被调用,允许开发者在bean完全初始化之前和之后执行自定义逻辑。

4.返回值:这两个方法都可以返回bean本身或一个代理对象。如果返回null,则Spring容器会忽略该bean,不再进行后续处理。

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;@Component
public class CustomBeanPostProcessor implements BeanPostProcessor {@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {// 在bean初始化之前可以进行的操作System.out.println("Before initialization: " + beanName);return bean; // 返回bean本身或修改后的bean}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {// 在bean初始化之后可以进行的操作System.out.println("After initialization: " + beanName);return bean; // 返回bean本身或修改后的bean}
}
<?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后置处理器 --><bean id="myBeanPostProcessor" class="com.example.MyBeanPostProcessor"/><!-- 定义其他bean --><bean id="myService" class="com.example.MyService"><!-- bean的配置 --></bean><!-- 其他bean定义 --></bean
使用场景:
  • 修改bean属性:在bean初始化前后修改其属性值。
  • 条件性地返回代理:根据某些条件返回一个代理对象,例如用于AOP(面向切面编程)。
  • 资源释放:在bean销毁之前执行清理工作。
  • 日志记录:记录bean的创建和销毁过程。

通过过程文件给bean注入值

<!--    配置monster--><context:property-placeholder location="classpath:my.properties"/><bean class="com.bean.Monster" id="monster10"><property name="monsterid" value="${monsterid}"/><property name="name" value="${name}"/><property name="skill" value="${skill}"/></bean>

创建一个配置文件

monsterid=1000
name=jack
skill=heal

自动装配bean

bytype------->通过类型

<!--    配置orderdao orderservice--><bean class="com.dao.OederDao" id="oederDao"/>
<!--    autowire = "bytype" orderservice时通过类型的方式给对象属性自动完成赋值-->
<!--    作用:在bean文件中寻找有没有orderdao类型的对象,有的化,就会自己完成装配-->
<!--    注意bean文件中不能有两个orderfao的类型--><bean  autowire="byType" class="com.service.OrderService" id="orderService"/>
<!--        手动装配-->
<!--        <property name="oederDao" ref="oederDao"/>--><bean autowire="byType" class="com.web.OrderAction" id="orderAction"/>

创建三个文件为:dao,service,selvet

byname---->通过名字完成自动装配

按照setxxx方法set后边的名字

spring EL表达式

界定符#{}

基于注解配置bean

基本使用

在Java编程中,注解(Annotations)是一种用于为代码提供元数据的机制。注解不会直接影响代码的执行逻辑,但可以被编译器、其他工具或运行时环境读取,从而提供额外的信息或指导。注解在很多方面被广泛使用,包括但不限于依赖注入、事务管理、日志记录、安全性检查等。

注解的基本概念:

  1. 1.声明注解:使用@interface关键字声明一个新的注解类型。

java

public @interface MyAnnotation {String value();
}
  1. 2.使用注解:在类、方法、变量等元素上使用注解。

java

@MyAnnotation(value = "Example")
public class ExampleClass {// ...
}
  1. 3.注解的保留策略:定义注解的生命周期,即它在源代码、字节码或运行时是否可用。通过@Retention注解指定。

java

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {// ...
}
  1. 4.注解的适用目标:通过@Target注解指定注解可以应用的目标类型。

java

import java.lang.annotation.Target;
import java.lang.annotation.ElementType;@Target(ElementType.METHOD)
public @interface MyAnnotation {// ...
}
  1. 5.注解的继承:注解默认不会被子类继承,但可以通过@Inherited注解指定注解可以被继承。

java

import java.lang.annotation.Inherited;@Inherited
public @interface MyAnnotation {// ...
}

常见的注解使用场景:

  • 依赖注入:Spring框架使用@Autowired@Qualifier等注解来实现依赖注入。

java

@Autowired
private MyService myService;
  • 事务管理@Transactional注解用于声明方法或类的事务边界。

java

@Transactional
public void performOperation() {// ...
}
  • RESTful Web服务:JAX-RS和Spring MVC使用注解来定义资源和路由。

java

@RestController
@RequestMapping("/api")
public class MyController {@GetMapping("/hello")public String sayHello() {return "Hello, World!";}
}
  • 单元测试:JUnit和TestNG使用注解来标记测试方法。

java

import org.junit.Test;
import static org.junit.Assert.*;public class ExampleTest {@Testpublic void testAddition() {assertEquals(2, 1 + 1);}
}
  • 日志记录:使用如@Log@Log4j等注解来简化日志记录。

java

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;public class MyClass {private static final Logger logger = LoggerFactory.getLogger(MyClass.class);public void myMethod() {logger.info("This is an info message");}
}

注解处理:

  • 编译时处理:注解处理器可以在编译时生成额外的源代码、资源文件或进行其他操作。
  • 运行时处理:在运行时,可以通过反射API读取注解信息并根据这些信息执行特定的逻辑。

基于注解的方式配置bean,主要是项目开发中的组件,比如Controller、Service、和Dao。

组件注解的形式有

  • 1.@Component 表示当前注解标识的是一个组件。
  • 2.@Controller 表示当前注解标识的是一个控制器,通常用于Servlet。
  • 3.@Service 表示当前注解标识的是一个业务逻辑的类,通常用于service类。
  • 4.@Repository 表示当前注解标识的是一个持久化层的类,通常用于Dao类。

spring框架的注解需要引入的jar包

1.spring-aop-5.3.8.jar

仿写注解

package com.annotation;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** @Author: zs* @Description: TODO* @DateTime: 2024/9/12 13:29**/
//规定当前自定义的的注解可以作用于那些类型
@Target(ElementType.TYPE)
//指定注解的作用范围
@Retention(RetentionPolicy.RUNTIME)public @interface ComponentScan {//表示我们的注解可以传入一个value属性String value() default "";
}
package com.annotation;import org.springframework.stereotype.Component;/*** @Author: zs* @Description: TODO* @DateTime: 2024/9/12 16:01**/
@ComponentScan(value = "com.component")
public class SpringConfig {}
package com.annotation;import com.applicationcontext.ApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;import javax.security.auth.login.Configuration;
import java.io.File;
import java.lang.annotation.Annotation;
import java.net.URL;
import java.util.concurrent.ConcurrentHashMap;/*** @Author: zs* @Description: TODO* @DateTime: 2024/9/13 12:46**/
public class SpringApplicationContext {//这个类的作业是模仿容器//需要拿到class类型,因为要使用反射//定义一个属性private Class configClass;//因为在框架内,bean的信息要放入容器的hashMap//所以这一要创建一个mapprivate final ConcurrentHashMap<String, Object> ioc =new ConcurrentHashMap<>();//构造器,在初始化容器时,选用传入beans.xml文件//public SpringApplicationContext(Class configClass) {//配置类不止在构造器使用,跨方法使用//拿到配置类的class类型,就可以到的注解//就可以拿到注解的value里的路径信息this.configClass = configClass;
//        System.out.println(this.configClass);//获取要扫描的包//1.先得到配置类的注解ComponentScan componentScan =(ComponentScan) this.configClass.getDeclaredAnnotation(ComponentScan.class);//通过comonentScan得到valueString path = componentScan.value();//获得value下的所有class的信息//1.得到类的加载器ClassLoader classLoader = ApplicationContext.class.getClassLoader();//2.通过加载器获得要扫描包的资源路径,class文件的信息不是在src而是在out//Java中解析路径识别不出. 所有要把查找出来com. 的路径转化为com/path = path.replace('.', '/');URL resource = classLoader.getResource(path);//3.将要加载的资料(.class)路径下的文件进行遍历//注意:如果要有子目录,需要进行递归File file = new File(resource.getFile());if (file.isDirectory()) {File[] files = file.listFiles();for (File f : files) {System.out.println(f.getAbsolutePath());String fileAbsolutePath = f.getAbsolutePath();//过滤,只处理.class文件if (fileAbsolutePath.endsWith(".class")) {//获取全类名,反射对象,放入容器中//1.获取类名String className = fileAbsolutePath.substring(fileAbsolutePath.lastIndexOf("\\") + 1, fileAbsolutePath.lastIndexOf(".class"));//2.获取类的路径String classFullName = path.replace("/", ".") + "." + className;//判断文件是不是需要注入容器中try {//这时我们得到该类的class对象//forname 可以通过反射加载类对象//loadclass 也可以//但是forname会调用该类的静态方法,Class<?> aClass = classLoader.loadClass(classFullName);if(aClass.isAnnotationPresent(Component.class) || aClass.isAnnotationPresent(Controller.class)|| aClass.isAnnotationPresent(Service.class) ||aClass.isAnnotationPresent(Repository.class)) {//指定value 分配idif(aClass.isAnnotationPresent(Component.class)) {Component component= aClass.getDeclaredAnnotation(Component.class);String id = component.value();if (!"".endsWith(id)) {className = id;}}//这里为什么用forname  会调用静态方法,会把类的联系加载进去Class<?> aClazz = Class.forName(classFullName);Object instance = aClazz.newInstance();//类的小写首字母作为idioc.put(StringUtils.uncapitalize(className), instance);}} catch (Exception e) {e.printStackTrace();}}}}}//编写方法返回容器对象public Object getBean(String beanName) {return ioc.get(beanName);}
}

注解自动装配

1.基于注解配置bean,也可实现自动装配,使用的注解是:@AutoWired 或者 @Resource

@AutoWired 的规则说明:

  1. 在IOC容器中查找待装配的组件的类型,如果有唯一的bean匹配,则使用该bean装配。

  2. 如待装配的类型对应的bean在IOC容器中有多个,则使用待装配的属性名作为id值再进行查找,找到就装配,找不到就抛异常。

  3. 操作流程

    <?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/contexthttp://www.springframework.org/schema/context/spring-context.xsd"><context:component-scan base-package="com.component"/></beans>
    

在IOC容器中查找待装配的组件的类型,如果有唯一的bean匹配,则使用该bean装配。

@Controller
public class UserAction {@Autowiredprivate UserService userService;public void sayOk() {System.out.println("UserAction");userService.hi();}
}
@Service
public class UserService {public void hi(){System.out.println("hi");}
}

如待装配的类型对应的bean在IOC容器中有多个,则使用待装配的属性名作为id值再进行查找,找到就装配,找不到就抛异常。

<?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/contexthttp://www.springframework.org/schema/context/spring-context.xsd"><context:component-scan base-package="com.component"/><bean class="com.component" id="userService200"/><bean class="com.component" id="userService300"/>
</beans>

3.@Resource 的规则说明:

  1. @Resource有两个属性是比较重要的,分别是name和type。Spring将@Resource注解的name属性解析为bean的名字,而type属性解析为bean的类型。所以如果使用name属性,则使用byName的自动注入策略;如果使用type属性,则使用byType的自动注入策略。

使用type时,对象必须是单例的]

  1. 如果@Resource没有name和type,则先使用byName注入策略,如果匹配不上,再使用byType策略,如果都不成功,就会报错。

建议,不管是@AutoWired 还是 @Resource,都应谨慎使用,确保理解其自动装配的规则和潜在的异常情况,以避免运行时错误。

@Controller
public class UserAction {@Resource(name = "userService200")private UserService userService;public void sayOk() {System.out.println("UserAction");System.out.println("useraction自动装配的" + userService);userService.hi();}
}

配置文件不变

泛型依赖注入

泛型:

  • 1.普通成员可以使用泛型(属性、方法)
  • 2.使用泛型的数组,不能初始化
  • 3.静态方法中不能使用类的泛型
  • 4.泛型类的类型,是在创建对象时确定的(因为创建对象时,需要指定确定类型)
  • 5.如果在创建对象时,没有指定类型,默认为Object

aop

动态代理

动态代理是 Java 中一种非常强大的特性,它允许在运行时创建一个实现了某个接口的代理对象,这个代理对象可以作为目标对象的替代品。动态代理在很多场景下非常有用,比如:

  1. 1.日志记录:在方法调用前后添加日志记录。
  2. 2.事务管理:在方法调用前后管理数据库事务。
  3. 3.安全检查:在方法调用前进行权限验证。
  4. 4.缓存:缓存方法的返回结果,避免重复计算。
  5. 5.延迟加载:实现对象的延迟加载机制。
  6. 6.远程方法调用:在远程方法调用(RMI)中,动态代理可以用来封装远程调用的细节。

动态代理的实现

在 Java 中,动态代理主要通过 java.lang.reflect.Proxy 类和 java.lang.reflect.InvocationHandler 接口来实现。

  • Proxy 类用于生成动态代理实例。
  • InvocationHandler 接口定义了代理实例上的方法调用处理程序。
package com.aop;import java.lang.reflect.*;
import java.util.Arrays;/*** @Author: zs* @Description: TODO* @DateTime: 2024/9/14 15:22**/
public class ProxyProvider {//创建一个目标对象,这个对象要实现接口private SmartAnimal smart_traget;public ProxyProvider(SmartAnimal smart_traget) {this.smart_traget = smart_traget;}//创建一个代理对象,我们要用代理对象如执行目标对象的方法public SmartAnimal getProxy() {//1.类加载器ClassLoader classLoader = smart_traget.getClass().getClassLoader();//2.目标接口信息Class<?>[] interfaces = smart_traget.getClass().getInterfaces();//3.创建一个InvocationHandlerInvocationHandler invocationHandler = new InvocationHandler() {public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Object result = null;try {//横切关注点System.out.println("方法执行前" + method.getName() + "参数" + Arrays.asList(args));//返回执行结果//使用反射方法调用方法result = method.invoke(smart_traget, args);//横切接入点System.out.println("方法执行后" + method.getName() + "结果" + result);} catch (Exception e) {e.printStackTrace();//如果执行方发时,出现异常,就会进入catchSystem.out.println("方法" + method.getName() + "执行异常 结果" + e.getClass().getName());} finally {//最中都会执行到这里//横切关注点,最终通知System.out.println("方法" + method.getName() + "名");}return result;}};//创建代理对象----动态代理类型SmartAnimal proxy = (SmartAnimal)Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);return proxy;}}

面向切面编程(Aspect-Oriented Programming,AOP)是一种编程范式,旨在将横切关注点(cross-cutting concerns)从业务逻辑中分离出来,以提高模块化。横切关注点是指那些影响多个类的问题,比如日志记录、事务管理、安全性等。AOP 通过创建切面(aspects)来实现这一点,切面可以定义在何处以及如何将额外的行为插入到程序代码中。

AOP 的关键概念:

  • 1.切面(Aspect):一个关注点的模块化,这个关注点可能会横切多个对象。事务管理是切面的一个典型例子。
  • 2.连接点(Join Point):在程序执行过程中某个特定的点,比如方法的调用或异常的抛出。在Spring AOP中,连接点总是方法的执行点。
  • 3.通知(Advice):在切面的某个特定的连接点上执行的动作。不同类型的通知包括“前置通知”(在方法调用之前)、“后置通知”(在方法成功执行之后)、“异常通知”(在方法抛出异常后)、“最终通知”(无论方法执行成功还是失败都会执行)和“环绕通知”(围绕方法执行的通知)。
  • 4.引入(Introduction):允许我们向现有的类添加新的方法或属性。
  • 5.织入(Weaving):把切面应用到目标对象并创建新的代理对象的过程。织入可以在编译时、加载时或运行时完成。

AOP 在 Spring 中的实现:

Spring AOP 是 Spring 框架的一部分,它使用代理模式来实现 AOP。Spring AOP 只支持方法级别的连接点,这意味着它只能在方法执行时进行拦截。

  • 使用 @Aspect 注解:在 Spring 中,你可以使用 @Aspect 注解来定义一个切面类。
  • 定义通知:使用 @Before@After@AfterReturning@AfterThrowing@Around 等注解来定义不同类型的通知。
  • 配置 AOP:在 Spring 配置中启用 AOP 支持,并指定哪些类或方法应用切面。
    <context:component-scan base-package="com.asp"/>
<!--    开启基于注解的AOP--><aop:aspectj-autoproxy/>

切入表达式:

通过表达式的方式定位一个或多个具体的连接点

execution[[权限修饰符] [返回类型] [简单类名/全类名] [方法名] [参数列表] ]

切入表达式的注意细节

1.切入表达式也可以指向类的方法,这时切入表达式会对该类/对象生效。
2.切入表达式也可以指向接口的方法,这时切入表达式会对实现了接口的类/对象生效。
3.切入表达式也可以对没有实现接口的类,进行切入.

动态代理机制1.jdk的proxy 面向接口 2.CGlib 是面向父类

两个动态代理的区别

  • 1.JDK动态代理是面向接口的,只能增强实现类中接口中存在的方法。CGlib是面向父类的,可以增强父类的所有方法。
  • 2.JDK得到的对象是JDK代理对象实例,而CGlib得到的对象是被代理对象的子类。

在类和切面类在一个包下可以简写类名

JoinPoint

public void beforeMethod(JoinPoint joinPoint){joinPoint.getSignature().getName(); // 获取目标方法名joinPoint.getSignature().getDeclaringType().getSimpleName(); // 获取目标方法所属类的简单类名joinPoint.getSignature().getDeclaringTypeName(); // 获取目标方法所属类的类名joinPoint.getSignature().getModifiers(); // 获取目标方法声明类型(public, private, protected)Object[] args = joinPoint.getArgs(); // 获取传入目标方法的参数,返回一个数组joinPoint.getTarget(); // 获取代理对象joinPoint.This(); // 获取代理对象自己
}

Aop切面优先级

@order(value=n) 来控制 n 值越小,优先级越高。

运行原理类似filter

基于xml配置Aop

bean.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: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/aop https://www.springframework.org/schema/aop/spring-aop.xsd"><!--    使用xml,完成aop编程--><bean class="com.aopxml.SmartAnimalAspect" id="animalAspect"/>
<!--    配置一个smartdog--><bean class="com.aopxml.SmartDog" id="smartDog"/>
<!--    配置切面类--><aop:config>
<!--        这里指定切面对象-->
<!--        <aop:aspect ref="animalAspect" order="10"/>--><!--配置切入点--><aop:pointcut id="myPointCut" expression="execution(public float com.aopxml.SmartDog.getSum(float,float))"/>
<!--        这里指定切面对象--><aop:aspect ref="animalAspect" order="10"><!--配置前置通知--><aop:before method="showBeginLog" pointcut-ref="myPointCut"/><!--返回通知--><aop:after-returning method="showSuccessEndLog" pointcut-ref="myPointCut" returning="res"/><!--异常通知--><aop:after-throwing method="showExceptionLog" pointcut-ref="myPointCut" throwing="throwable"/><!--最终通知--><aop:after method="showFinallyEndLog" pointcut-ref="myPointCut"/><!--            <aop:around method=""--></aop:aspect></aop:config>
</beans>

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com