Spring
1.1 简介
- Spring:春天------>给软件行业带来了春天!
- 2002,首次推出了Spring框架的雏形:interface21框架!
- Spring框架即以interface21框架为基础,经过重新设计,并不断丰富其内涵,于2004年3月24日发布了1.0正式版。
- Rod Johnson,Spring Framework创始人,著名作者。很难想象Rod Johnson的学历,真的让好多人大吃一惊,他是悉尼大学的博士,然而他的专业不是计算机,而是音乐学。
- Spring理念:是为了解决企业级应用开发的复杂性而创建的,简化开发,本身是一个大杂烩,整合了现有的技术框架!
- SSH:Struct2 + Spring + Hibernate!
- SSM:SpringMVC + Spring + Mybatis!
官网:https://spring.io/projects/spring-framework#overview
官方下载地址:https://repo.spring.io/release/org/springframework/spring/
GitHub:https://github.com/spring-projects/spring-framework
maven
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<!--这个依赖里面东西比较多,相当于导了很多个需要的包-->
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.6</version>
</dependency>
Spring是一个免费的开源框架
Spring是一个轻量级、非入侵式的框架
控制反转(IOC)面向切面编程(AOP)
支持事务的处理,对框架整合的支持
Spring是一个轻量级的控制反转和面向切面编程的框架
弊端:由于发展了太久,容纳的技术越来越繁多,导致配置十分繁琐
Spring的核心Ioc介绍:
-
依赖:CalssA使用了ClassB的属性或者方法,ClassA依赖ClassB。
-
Ioc: 控制反转,是一个概念,是一个思想。用来指导我们如何创建、管理、使用对象的。
-
控制:控制对象的创建,属性的赋值,依赖关系的管理。以及对象从创建到销毁的整个生命周期。
-
反转:把开发人员在代码中创建到对象的权限转移给代码之外的容器(Spring)实现,由容器实现创建对象,管理。
-
正转:源代码中由开发人员,使用new 构造方法创建对象。
Ioc简单的说就是由别人创建对象,自己在代码中使用已经创建好的对象。Spring是容器,它可以创建和管理对象,管理依赖关系
- DI 在 Spring 上通过反射机制来实现 Ioc
- 配置文件中所有的对象,会在容器创建时创建出来
- Spring容器底层是一个Map,bean的id和class属性是以 K V 的方式存放在这个Map集合中的
- 实体类对象一般不交给容器来创建。Servlet对象是属于TomCat来创建的,不能交给Spring容器来创建
- Spring容器默认是使用类的无参构造来创建对象的
Bean有参构造赋值
实体类
public class ThingOne {
private ThingTwo two;
private ThingThree three;
//没有无参构造
public ThingOne(ThingTwo two,ThingThree three){
this.two = two;
this.three = three;
System.out.println("有参构造");
}
}
public class ThingTwo {
public ThingTwo(){
System.out.println("two");
}
}
public class ThingThree {
public ThingThree(){
System.out.println("three");
}
}
2
public class IndexBean {
private int id;
private String name;
public IndexBean(int id, String name) {
this.id = id;
this.name = name;
}
}
public class exampleBean {
private int id;
private String name;
public exampleBean(int id, String name) {
this.id = id;
this.name = name;
}
}
applicationContext.xml
<!--默认无参构造-->
<bean id="serviceImpl" class="com.zsh.service.UserServiceImpl">
<property name="dao" ref="userImpl"/>
</bean>
<!--ThingTest使用有参构造方法 -->
<!--1、参数为引用类型-->
<bean id="one" class="com.zsh.pojo.ThingOne">
<constructor-arg ref="two"/>
<constructor-arg ref="three"/>
</bean>
<bean id="two" class="com.zsh.pojo.ThingTwo">
</bean>
<bean id="three" class="com.zsh.pojo.ThingThree">
</bean>
<!--2、基本类型参数,name在参数较少的情况下可以省略-->
<bean id="example" class="com.zsh.pojo.exampleBean">
<constructor-arg type="int" name="id" value="121"/>
<constructor-arg type="java.lang.String" name="name" value="你好"/>
</bean>
<!--3、根据下标赋值-->
<bean name="index" class="com.zsh.pojo.IndexBean">
<constructor-arg index="0" value="1"/>
<constructor-arg index="1" value="张三"/>
</bean>
测试
@Test
public void test1(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserServiceImpl serviceImpl = (UserServiceImpl)context.getBean("serviceImpl");
serviceImpl.getUser();
}
@Test
public void ThingTest1(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
ThingOne one = (ThingOne) context.getBean("one");
}
@Test
public void ThingTest2(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
exampleBean one = (exampleBean) context.getBean("example");
System.out.println(one);
}
@Test
public void ThingTest3(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
IndexBean one = (IndexBean) context.getBean("index");
System.out.println(one);
}
别名
<!--如果添加了别名,通过别名也可以获取对象-->
<alias name="user" alias="userAlias"/>
Bean的配置
<!--
id: bean的唯一标识符,也就是相当于我们学的对象名
class: bean对象的全限定名:包名 + 类型
name: 也是别名 而且name可以同时设置多个别名,可以用逗号 空格 分号隔开
-->
<bean id="user1" class="com.yang.entity.User" name="test test1, test2; test3">
<constructor-arg type="int" value="18"/>
<constructor-arg type="java.lang.String" value="张三"/>
</bean>
import
import,一般用于团队开发使用,他可以将多个配置文件,导入合并为1个
假设,现在项目中又多个人开发,这三个人负责不同的类开发,不同的类需要注册在不同的bean中,我们可以用import将所有人的beans.xml合并为一个总的!
- appliacationContext1.xml
- appliacationContext2.xml
- appliacationContext3.xml
- 合并
<import resource="applicationContext1.xml"/>
Spring DI(依赖注入)
构造器和set注入
环境
public class Student {
private String name;
private Address address;
private String[] books;
private List<String> hobbys;
private Map<String,String> card;
private Set<String> games;
private String girlFriends;
private Properties info;
}
public class Address {
private String address;
public Address() {
}
}
<?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="student" class="com.zsh.pojo.Student">
<!--基本类型注入-->
<property name="name" value="猪鼻"/>
<!--引用类型注入-->
<property name="address" ref="address"/>
<!--数组注入-->
<property name="books">
<array>
<value>java修炼手册</value>
<value>mysql修炼手册</value>
<value>spring boot修炼手册</value>
<value>spring cloud修炼手册</value>
</array>
</property>
<!--List注入-->
<property name="hobbys">
<list>
<!--如果要添加引用类型使用<ref>标签-->
<value>吃饭</value>
<value>吃饭</value>
<value>吃饭</value>
</list>
</property>
<!--Map注入-->
<property name="card">
<map>
<entry key="招商银行" value="543123543213"/>
<entry key="农业银行" value="643123543213"/>
<entry key="邮储银行" value="743123543213"/>
<entry key="工商银行" value="843123543213"/>
</map>
</property>
<!--Set-->
<property name="games">
<set>
<value>LOL</value>
<value>aa</value>
<value>bb</value>
</set>
</property>
<!--设置null值-->
<property name="girlFriends">
<null/>
</property>
<!--给properties注入k-v-->
<property name="info">
<props>
<prop key="学号">38</prop>
<prop key="成绩">208</prop>
<prop key="¥">8</prop>
</props>
</property>
</bean>
<bean id="address" class="com.zsh.pojo.Address">
<property name="address" value="山东菏泽曹县"/>
</bean>
</beans>
测试
@Test
public void test1(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Student student = (Student) context.getBean("student");
System.out.println(student);
}
拓展注入
官方文档位置
pojo增加User类
package pojo;
public class User {
private String name;
private int id;
public User() {
}
public User(String name, int id) {
super();
this.name = name;
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
@Override
public String toString() {
return "User [name=" + name + ", id=" + id + "]";
}
}
注意: beans 里面加上这下面两行
使用p和c命名空间需要导入xml约束
xmlns:p=“http://www.springframework.org/schema/p”
xmlns:c=“http://www.springframework.org/schema/c”
使用方式
?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:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--p命名空间注入/set注入,可以直接注入属性的值-》property-->
<bean id="user" class="pojo.User" p:name="cxk" p:id="20" >
</bean>
<!--c命名空间,通过构造器注入,需要写入有参和无参构造方法-》construct-args-->
<bean id="user2" class="pojo.User" c:name="cbh" c:id="22"></bean>
</beans>
测试
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
User user = context.getBean("user",User.class);//确定class对象,就不用再强转了
System.out.println(user.toString());
Bean作用域
单例模式(默认)
<bean id="user2" class="pojo.User" c:name="cxk" c:age="19" scope="singleton"></bean>
弹幕评论:单例模式是把对象放在pool中,需要再取出来,使用的都是同一个对象实例
原型模式: 每次从容器中get的时候,都产生一个新对象!
<bean id="user2" class="pojo.User" c:name="cxk" c:age="19" scope="prototype"></bean>
其余的request、session、application这些只能在web开放中使用!
bean自动注入
需要在bean标签中加入auto属性
<bean id="name" class="x.x.xxx.class" autowire=""/>
autowired可配置的属性
-
byName
需要注入的bean id 必须和 本类 中set方法的属性名一致;id唯一
<bean class="com.zsh.pojo.Dog" id="dog"/> <bean class="com.zsh.pojo.Cat" id="cat"/> <bean id="people" class="com.zsh.pojo.People" autowire="byName"> <property name="name" value="张三"/> </bean>
-
byType
根据bean类型来匹配,整个xml文件必须只有一个被匹配的bean,否则报错;class唯一
<bean class="com.zsh.pojo.Dog" /> <bean class="com.zsh.pojo.Cat" /> <bean id="people" class="com.zsh.pojo.People" autowire="byType"> <property name="name" value="张三"/> </bean>
@Autowired
xml文件中加入:
<context:annotation-config/>
(翻译:基于注释的配置的引入提出了一个问题,即这种方法是否比XML“更好”)
导入context约束
xmlns:context="http://www.springframework.org/schema/context"
配置注解的支持:< context:annotation-config/>
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
</beans>
默认是byType方式,如果匹配不上,就会byName
在属性上使用,也可以在set上使用
我们可以不用编写set方法了,前提是自动装配的属性在Spring容器里,且要符合ByName 自动装配
public class People {
@Autowired
private Cat cat;
@Autowired
private Dog dog;
private String name;
}
@Nullable
字段标记了这个注解,说明该字段可以为空
public name(@Nullable String name){
}
如果定义了Autowire的require属性为false,说明这个对象可以为null,否则不允许为空(false表示找不到装配,不抛出异常)
//源码
public @interface Autowired {
boolean required() default true;
}
@Autowired+@Qualifier
@Autowired不能唯一装配时,需要@Autowired+@Qualifier
如果@Autowired自动装配环境比较复杂。自动装配无法通过一个注解完成的时候,可以使用@Qualifier(value = “dog”)去配合使用,指定一个唯一的id对象
public class People {
@Autowired
private Cat cat;
@Autowired
@Qualifier(value = "dog")
private Dog dog;
private String name;
}
弹幕评论:
如果xml文件中 同一个 对象 被 多个bean 使用,Autowired无法按 类型 找到,可以用@Qualifier 指定id 查找
@Resource
默认以ByName的方式匹配,如果找不到匹配的bean id,则根据ByType的方式查找,如果类型又有多个,可以给Resource一个参数
指定id查找
public class People {
Resource(name="cat")
private Cat cat;
Resource(name="dog")
private Dog dog;
private String name;
}
区别:
@Resource和@Autowired的区别:
- 都是用来自动装配的,都可以放在属性字段上
- @Autowired通过先byType后byName的方式实现,【常用】
- @Resource默认通过先byName后byType的方式实现,如果找不到名字,则通过byType实现!如果两个都找不到的情况下,就报错!【常用】
- 执行顺序不同:@Autowired通过byType的方式实现。@Resource默认通过byname的方式实现
@Component
意指作为组件被Spring扫描,等价于在xml文件中注册了bean
@Component
public class User {
public String name ="猪鼻";
}
DAO【@Repository】
Service【@Service】
Controller【@Controller】
四个注解分别代表不同的层,但大意相同,代表将当前类注册到Spring中,装配bean
@Scope【作用域】
作用域
@Scope("指定为单例模式")
// 默认是单实例的
/**
* ConfigurableBeanFactory#SCOPE_PROTOTYPE [prototype]
* ConfigurableBeanFactory#SCOPE_SINGLETON [singleton]
* @return
* prototype:多实例的: ioc容器启动并不会去调用方法创建对象放到IOC容器中
* 每次获取的时候才会调用方法创建对象
* singleton:单实例的(默认的): ioc容器启动会调用方法创建对象放到IOC容器中
* 以后每次获取就是直接从容器(map.get())中拿
* 懒加载
* 单实例bean,默认在容器启动的时候创建对象
* 懒加载:容器启动不创建对象,第一次使用(获取)Bean创建对象,并初始化
*/
// @Scope("prototype") //调整作用域
public class User{}
小结:
xml与注解:
- xml更加万能,适用于所有场合,且维护简单方便
- 注解中不是自己管理的类使用不了
xml与注解的最佳实践:
- xml只用来管理bean
- 注解只负责完成属性的注入
- 记得开启注解的支持,和扫描包
<context:component-scan base-package="com.zsh.pojo"/>
<context:annotation-config/>
@Bean
@Bean(“getUser”)
有参数设置被扫描的bean名字
@Import
作用:
可以将两个配置类做为一个ioc容器
有参数可设置为被引入的配置类
@CompenentSacn
有参数指定扫描的包位置,扫描带有@Conponent的注解作为bean
@Configuration
配置类上加上这个注解表示:这个类作为配置文件,里面的@Bean注解相当于,加上@ComponentScan相当于在配置文件中开启了注解扫描
-
Configuration的主要功能是用来注册Bean,下面是最简单的使用,注册一个Bean,然后使用这个Configuration注册/初始化Spring容器,然后就可以通过getBean来获取了
-
从源码能够看到,@Configuration上面有@Component注解,也就是说他其实也是一个Spring Bean,我们也可以通过getBean获取他,不过这个Bean有个特殊的地方是他的构造方法可以不使AutoWired注解(还可以注入Environment),就能自动隐式注入。
@Configuration public class Main1 { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.register(Main.class,Main1.class); context.refresh(); Main.MyBean mybean = context.getBean(Main.MyBean.class); System.out.println(mybean); Main1 bean = context.getBean(Main1.class); System.out.println(bean.myBean); System.out.println(bean.environment); } public Main.MyBean myBean; public Environment environment; public Main1(Main.MyBean myBean, Environment environment){ this.myBean = myBean; this.environment = environment; } }
总结注解创建ioc容器的两种方式
前提:先在配置类中加入@Configuration注解
-
在配置类中定义一个方法,并使用@Bean标注
-
在需要作为bean的类上加入@Component注解,并在配置类上标注@ComponentScan(“需要被扫描的包”),
这样会自动扫描带有@Component注解的类,作为bean
@Configuration VS @Component
- 共同点:都可以用于创建Bean;
- 不同点:实现原理不同,@Configuration基于CGlib代理实现,@Component基于反射实现;
- 使用场景:@Configuration用于全局配置,比如数据库相关配置,MVC相关配置等;业务Bean的配置使用注解配置(@Component,@Service,@Repository,@Controller)。
代理模式
静态代理
- 代理模式主要做的事是帮被代理的类去处理一些繁琐、公共的业务,原来的类只需要专注自己的功能就好
- 实现业务分工
- 方便集中管理,因为每个功能都分离出来了
- 尽量使用多态的方式
缺点:一个角色就会产生一个代理,造成代码冗余
一个小demo
– 要实现的功能
public interface Rent {
//接口方法
public void rent();
}
– 实现功能的类
public class Host implements Rent {
@Override
public void rent() {
System.out.println("房东出租房间");
}
}
– 代理
public class Proxy implements Rent{
private Rent rent;
public Proxy() {
}
// 代理的类可以为原来的类做一些其他的工作
public Proxy(Rent rent) {
fee();
this.rent = rent;
signContract();
}
@Override
public void rent() {
rent.rent();
}
public void fee(){
System.out.println("收取费用");
}
public void signContract(){
System.out.println("签合同");
}
}
– 测试
public class Client {
public static void main(String[] args) {
Proxy proxy = new Proxy(new Host());
proxy.rent();
}
}
动态代理【jdk】
所有动态代理的底层都是反射
public class ProxyInvocationHandler implements InvocationHandler {
/*
一个万能的动态代理工具类,
根据使用者传过来的被代理接口作为实现
*/
private Object target;
public void setRent(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//动态代理的本质就是使用反射机制实现
//此处做一些代理外接繁琐功能的实现
//根据传过来的接口,再根据参数去匹配对应的方法去执行
Object result = method.invoke(target, args);
return result;
}
//生成得到代理类
public Object getProxy() throws Throwable {
/*
方法需要的参数
ClassLoader loader 类加载器,生成的类作为target的实现
类<?>[] interfaces 需要代理的接口,使用引入当前类再使用反射获取接口target
InvocationHandler 处理器 当前类实现了 InvocationHandler 就是一个处理器
* */
return Proxy.newProxyInstance(target.getClass().getClassLoader()
,target.getClass().getInterfaces(), this);
}
}
动态代理的优点
- 有静态代理的所有优点
- 一个动态代理,代理的是一个接口,一般就是 对应的 一类业务
- 一个动态代理类可以代理多个类,只要是实现了同一个接口即可
博客解释
使用JDK的动态代理去生成代理只需要一行代码:
Moveable move = (Moveable) Proxy.newProxyInstance(Car.class.getClassLoader(),
Car.class.getInterfaces(), new LogHandler(new Car()));
传入的参数中其实就俩,一是被代理类的类对象,二是自定义的增强处理代码。下面看下LogHandler源码:
public class LogHandler implements InvocationHandler{
private Object target;
public LogHandler(Object object){
super();
this.target = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //增强处理
Object o = method.invoke(target,args);
return o;
}
}
- 观察InvocationHandler实现类的源码可以发现,首先是定义了一个含参构造方法,该参数即为要代理的实例对象,观察target对象的使用位置在method.invoke()方法的参数中,不难看出,目的也就是为了执行目标方法。那重写的invoke()方法的三个参数又是什么呢?顾名思义,
- proxy:动态生成的代理对象
- method:目标方法的实例
- args:目标方法的参数
- 从Proxy.newProxyInstance()的方法参数中包含了被代理类的接口的类对象也不难得知,JDK的动态代理只能代理实现了接口的类, 没有实现接口的类不能实现动态代理。
关于Jdk动态代理的原理,我归纳得出四步
- 声明一段源码,源码动态产生动态代理
- 源码产生java文件,对java文件进行编译
- 得到编译生成后的class文件
- 把class文件load到内存之中,产生代理类对象返回即可。
cglib的动态代理
cglib使用动态代理的流程:
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
//设置被代理类
enhancer.setSuperclass(Car.class);
//设置回调函数
enhancer.setCallback(new MethodInterceptor() { @Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { //增强处理... //增强处理...
Object o= proxy.invokeSuper(obj, args);//代理类调用父类的方法
//增强处理...
return o;
}
});
//创建代理类并使用回调
Car car = (Car) enhancer.create();
//执行目标方法
System.out.println(car.move());
}
从上面的代码看到,cglib的使用流程还是很清晰明了,各种参数顾名思义,和jdk的区别不大。生成的代理对象直接被该类引用,与我们认知的基于继承的动态代理没冲突。不过这种基于继承的方式就没有什么缺点吗?最明显的一点就是final修饰的类无法使用。
Spring用的啥
Jdk的动态代理和cglib的动态代理你都已经知晓,spring底层究竟是如何选择哪种动态代理方式,这也是面试经常考到的一个知识点。
标准回答如下:Spring会根据具体的Bean是否具有接口去选择动态代理方式,如果有接口,使用的是Jdk的动态代理方式,如果没有接口,使用的是cglib的动态代理方式。
AOP
什么是AOP
AOP在Spring中使用
提供声明式事务,允许用户自定义切面
-
横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。
日志,安全,缓存,事务等等…
-
切面(Aspect):横切关注点 被模块化的特殊对象。即,它是一个类。(Log类)
-
通知(Advice):切面必须要完成的工作。即,它是类中的一个方法。(Log类中的方法)
-
目标(Target):被通知对象。(生成的代理类)
-
代理(Proxy):向目标对象应用通知之后创建的对象。(生成的代理类)
-
切入点(PointCut):切面通知执行的”地点”的定义。(最后两点:在哪个地方执行,比如:method.invoke())
-
连接点(JointPoint):与切入点匹配的执行点。
SpringAOP中,通过Advice定义横切逻辑,Spring中支持5种类型的Advice【通知】:
即AOP在不改变原有代码的情况下,去增加新的功能。(代理)
SpringAPI实现AOP
- 导入依赖
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.5</version>
</dependency>
- 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
http://www.springframework.org/schema/aop/spring-aop.xsd">
</beans>
- SpringAPI实现
//After实现后置通知接口
public class AfterLog implements AfterReturningAdvice {
@Override
public void afterReturning(@Nullable Object returnValue, Method method, Object[] args, @Nullable Object target) throws Throwable {
System.out.println(target.getClass().getName()+"执行完毕,返回参数:"+returnValue);
}
}
//Before
public class Log implements MethodBeforeAdvice {
/*
method: 要执行的目标对象的方法
args : 参数,根据参数匹配方法
target: 目标对象
*/
@Override
public void before(Method method, Object[] args, @Nullable Object target) throws Throwable {
System.out.println(target.getClass().getName()+"的"+method.getName()+"被执行了.....");
}
}
- applicationContext.xml【先看后面的execution表达式】
<!--注册bean-->
<bean id="userservice" class="service.UserServiceImpl"/>
<bean id="log" class="log.Log"/>
<bean id="afterLog" class="log.AfterLog"/>
<!--方式1:使用原生API-->
<!--配置aop:需要导入约束-->
<aop:config>
<!--切入点:expression:表达式,execution(要执行的位置)-->
<!--UserServiceImpl.*(..) -》 UserServiceImpl类下的所以方法(参数)-->
<aop:pointcut id="pointcut" expression="execution(* com.zsh.service.UserServiceImpl.*(..))"/>
<!--给前置通知添加切入点-->
<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
<!--给后置通知添加切入点-->
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
</aop:config>
测试
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService service = context.getBean("userService", UserService.class);
service.add();
自定义切面实现AOP
public class DiyPointCut {
public void before(){
System.out.println("before");
}
public void after(){
System.out.println("after");
}
}
xml配置相关信息
<!--方式2:自定义-->
<bean id="diyLog" class="com.zsh.diy.DiyPointCut"/>
<aop:config>
<!--
定义切面
ref:引用的切面类
-->
<aop:aspect ref="diyLog">
<!--定义切入点-->
<aop:pointcut id="point" expression="execution(* com.zsh.service.UserServiceImpl.*(..)) "/>
<!--前置通知-->
<aop:before method="before" pointcut-ref="point"/>
<!--后置通知 returning:返回值-->
<aop:after-returning method="after" pointcut-ref="point" />
</aop:aspect>
</aop:config>
测试
@Test
public void test1(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService service = context.getBean("userService", UserService.class);
service.add();
}
注解实现AOP
创建切面类
@Aspect
public class AnnotationPointCut {
@Before("execution(* com.zsh.service.UserServiceImpl.*(..))")
public void Before(){
System.out.println("方法执行前......");
}
@After("execution(* com.zsh.service.UserServiceImpl.*(..))")
public void after(){
System.out.println("方法执行后......");
}
//在增强环绕中,可以给定义一个参数【ProceedingJoinPoint】,代表要处理的切入点类
@Around("execution(* com.zsh.service.UserServiceImpl.*(..))")
public void around(ProceedingJoinPoint jp) throws Throwable {
System.out.println("方法环绕前");
//执行方法
Object proceed = jp.proceed();
System.out.println("方环绕后");
}
}
xml配置
<!--方式3:注解实现AOP-->
<bean id="annotationPointCut" class="com.zsh.diy.AnnotationPointCut"/>
<!--
开启自动代理
实现方式
默认JDK (proxy-targer-class="fasle")
cgbin (proxy-targer-class="true")
-->
<aop:aspectj-autoproxy/>
测试
@Test
public void test1(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService service = context.getBean("userService", UserService.class);
service.add();
}
Spring-Mybatis
spring整合mybatis【方式1】
官方文档:https://mybatis.org/spring/zh/【能看懂!】
应该使用SqlSessionTemplate因为线程安全,可重用性高
**===============================================================**
导入jar包和扫描xml文件
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.9</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.7</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.4</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.2</version>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
demo结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XKZgByST-1624634180530)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210525191915522.png)]
编写顺序:
User -> UserMapper -> UserMapper.xml -> spring-dao.xml -> UserServiceImpl -> applicationContext.xml -> MyTest
实体类:
对应mysql表
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Integer id;
private String name;
private String pwd;
}
UserMapper
public interface UserMapper {
public List<User> queryAll();
}
UserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--命名空间要写全限定路径-->
<mapper namespace="com.zsh.mapper.UserMapper">
<select id="queryAll" resultType="user">
SELECT * FROM mybatis.user
</select>
</mapper>
【复用死代码】
spring-dao.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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--使用Spring提供的jdbc:org.springframework.jdbc.DataSource-->
<!--设置数据源-->
<bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<!--用户名-->
<property name="username" value="root"/>
<!--密码-->
<property name="password" value="123456"/>
<!--数据库url-->
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8"/>
<!--数据库连接驱动-->
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
</bean>
<!--SqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--mybatis-config的所有配置都能在这里实现-->
<!--指定数据源-->
<property name="dataSource" ref="datasource" />
<!--引用mybatis-config,也可以不引用,这里可以配置所有在mybatis配置过的信息-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<!--指定接口与数据库交互的Mapper.xml文件和引用的mybatis文件不能有相同的配置-->
<property name="mapperLocations" value="classpath:com/zsh/mapper/*.xml"/>
</bean>
<!--通过注入一个SqlSessionFactory来创建sqlSession-->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<!--因为SqlSessionTemplate没有Set方法,所以只能以构造方法注入-->
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
<!--真正执行操作的类,注入属性sqlSession-->
<bean id="userMapper" class="com.zsh.mapper.UserMapperImpl">
<property name="sqlSession" ref="sqlSession"/>
</bean>
</beans>
mybatis-config
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration >
<typeAliases>
<package name="com.zsh.pojo"/>
</typeAliases>
<mappers>
<!--Spring和mybatis-config中相同的配置不能同时存在-->
<!--<mapper class="com.zsh.mapper.UserMapper"/>-->
<!--<package name="com.zsh.mapper"/>-->
</mappers>
</configuration>
SqlSessionTemplate
与sqlSession作用相同,SqlSessionTemplate线程安全,且spring推荐使用
private SqlSessionTemplate sqlSession;
public void setSqlSession(SqlSessionTemplate sqlSession) {
this.sqlSession = sqlSession;
}
@Override
public List<User> queryAll() {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> list = mapper.queryAll();
return list;
}
测试
@Test
public void test1() throws IOException {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-dao.xml");
UserMapper mapper = context.getBean("userMapper", UserMapper.class);
List<User> list = mapper.queryAll();
for (User user : list) {
System.out.println(user);
}
}
spring整合mybatis【方式2】
使用接口实现类测试
//通过继承SqlSessionDaoSupport 来代理获得SqlSession
public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper {
@Override
public List<User> queryAll() {
SqlSession sqlSession = getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
return mapper.queryAll();
}
}
引用前面xml文件中的SqlSessionFactory来创建SqlSession实例
<import resource="spring-dao.xml"/>
<!--
继承SqlSessionDaoSupport之后需要注入一个sqlSessionFactory
作为创建SqlSessionTemplate的条件
省略了创建sqlSession的步骤
-->
<bean id="userMapper2" class="com.zsh.mapper.UserMapperImpl2">
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
测试
@Test
public void test1() throws IOException {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserMapper mapper = context.getBean("userMapper2", UserMapper.class);
for (User user : mapper.queryAll()) {
System.out.println(user);
}
}
execution表达式
execution(修饰符 返回值 包名.类名/接口名.方法名(参数列表))注意老师忽略掉修饰符了 自己可以写上修饰符试试
(..)可以代表所有参数,(*)代表一个参数,(*,String)代表第一个参数为任何值,第二个参数为String类型.
<aop:aspect ref="diyLog">
<aop:pointcut id="point" expression="execution(* com.zsh.service.UserServiceImpl.*(..)) "/>
</aop:aspect>
Spring事务
- 把一组业务当成一个业务来做;要么都成功,要么都失败!
- 事务在项目开发中,十分的重要,涉及到数据的一致性问题
- 确保完整性和一致性
事务的ACID原则:
1、原子性
2、隔离性
3、一致性
4、持久性
ACID参考文章:https://www.cnblogs.com/malaikuangren/archive/2012/04/06/2434760.html
Spring中的事务管理
有两种方式
- 声明式事务:AOP
- 编程式事务:需要再代码中,进行事务管理
声明式事务【方式1;推荐】
1. 导入依赖/jar包
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.9</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.7</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.4</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.2</version>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
2.spring-dao.xml【重点】
请认真阅读注释理解
<?xml version="1.0" encoding="UTF-8"?>
<!--这里需要导入命名空间 tx!!!-->
<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"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
https://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--使用Spring提供的jdbc:org.springframework.jdbc.DataSource-->
<!--设置数据源-->
<bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<!--用户名-->
<property name="username" value="root"/>
<!--密码-->
<property name="password" value="123456"/>
<!--数据库url-->
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8"/>
<!--数据库连接驱动-->
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
</bean>
<!--SqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--mybatis-config的所有配置都能在这里实现-->
<!--指定数据源-->
<property name="dataSource" ref="datasource" />
<!--引用mybatis-config-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<!--指定接口与数据库交互的Mapper.xml文件-->
<property name="mapperLocations" value="classpath:com/zsh/mapper/*.xml"/>
</bean>
<!--通过注入一个SqlSessionFactory来创建sqlSession-->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<!--因为SqlSessionTemplate没有Set方法,所以只能以构造方法注入-->
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
========================================================================================
<!--配置事务管理器,声明式事务-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--配置dataSource-->
<property name="dataSource" ref="datasource"/>
</bean>
<!--结合aop实现事务织入-->
<!--配置事务的通知类-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!--配置事务的传播特性 propagation-->
<tx:attributes>
<!--指定扫描的方法-->
<!--<tx:method name="insert" propagation="REQUIRED"/>-->
<!--让select方法只读数据-->
<tx:method name="query" read-only="true"/>
<!--可以直接扫描所有方法,添加事务-->
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!--配合上面的事务切入-->
<aop:config>
<!--设置切入点-->
<aop:pointcut id="txPointCut" expression="execution(* com.zsh.mapper.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
</aop:config>
</beans>
mybatis-config.xml
它的存在代表这是一个整合了mybatis的项目^^
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration >
<typeAliases>
<package name="com.zsh.pojo"/>
</typeAliases>
<mappers>
<!--Spring和mybatis-config中相同的配置不能同时存在-->
<!--<mapper class="com.zsh.mapper.UserMapper"/>-->
<!--<package name="com.zsh.mapper"/>-->
</mappers>
</configuration>
搭建demo结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AvMb0KNn-1624634180533)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210526104413968.png)]
User实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Integer id;
private String name;
private String pwd;
}
UserMapper接口
public interface UserMapper {
// 给定三个方法用于测试
public int insertUser(User user);
public int deleteUserById(Integer id);
public List<User> queryAll();
}
UserMapperImpl实现类
public class UserMapperImpl implements UserMapper {
private SqlSessionTemplate sqlSession;
public void setSqlSession(SqlSessionTemplate sqlSession) {
this.sqlSession = sqlSession;
}
@Override
public int insertUser(User user) {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
return mapper.insertUser(user);
}
@Override
public int deleteUserById(Integer id) {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
return mapper.deleteUserById(id);
}
@Override
public List<User> queryAll() {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = new User(null, "老八", "13213");
insertUser(user);
deleteUserById(7);
List<User> list = mapper.queryAll();
return list;
}
}
UserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--命名空间要写全限定路径-->
<mapper namespace="com.zsh.mapper.UserMapper">
<select id="queryAll" resultType="user">
SELECT * FROM mybatis.user
</select>
<insert id="insertUser" parameterType="user" >
INSERT INTO mybatis.user(id,name,pwd) VALUES(#{id},#{name},#{pwd})
</insert>
<delete id="deleteUserById" parameterType="int" >
DELETE FROM mybatis.user WHERE id = #{id}
</delete>
</mapper>
applicationContext.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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<import resource="spring-dao.xml"/>
<bean id="userMapper" class="com.zsh.mapper.UserMapperImpl">
<property name="sqlSession" ref="sqlSession"/>
</bean>
</beans>
测试
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserMapper mapper = context.getBean("userMapper", UserMapper.class);
for (User user : mapper.queryAll()) {
System.out.println(user);
}
}
}
小结
声明式事务配合切面来完成事务的构建,在不改动原来代码的情况加添加事务功能,
增删改执行过程中出现异常会回滚数据,从而保证数据的ACID
spring-dao.xml【仔细阅读】
编码式事务【方式2】
通过在增删改的代码之前添加开启事务的代码,并且在try cache.....语句中捕捉异常,回滚事务
这个方式也可以,但是只建议在项目构建之处使用,避免修改源码的问题
思考:
为什么需要事务?
- 如果不配置事务,可能存在数据提交不一致的情况下;
- 如果不在spring中去配置声明式事务,我们就需要在代码中手动配置事务!
- 事务在项目的开发中非常重要,涉及到数据的一致性和完整性问题!
Spring项目基本操作
创建Spring的容器对象是根据Spring配置文件所在的位置,使用接口来调用不同的实现类
如果是类路径(classpath),使用ClassPathApplicationContext();
如果是项目根目录,使用FileSystemXmlApplicationContext();
bean对象的作用域,作用域是对象的存在范围和可见性
1、单例: singleton ,bean对象默认为单例,表示叫这个名称的对象在Spring容器中只有一个
指定单例的语法:
创建容器时,会创建所有单例对象
2、DI实现有两种语法:
1)、使用xml文件中的标签和属性,
2)、使用注解
Di的分类:
1)、设值注入
简单类型的设值注入:就是Spring调用实体类的set方法,完成赋值,最常用的方式
引用类型的设值注入:1)、ref作为属性 2)、ref作为子标签 。新建一个bean作为需要被赋值的 ref 内容
2)、构造注入
Spring去调用实体类的有参数构造方法,完成属性赋值。使用较少
简单类型的设值注入:
1、,每个
标签只能为一个属性赋值,在标签中生效。
如果需要同时给多个属性赋值,需要编写多个标签
2、使用xml注入赋值时,Spring关系的只有set方法。赋值时只要property中的属性能在实体类中找到对应的 set 方法,不管有没有sex这个成员变量(属性),不会违反Spring的约定。
3)、自动注入(autoWrite):
ByName(按属性名注入):
类中的属性名称与配置文件(xml)中bean标签的id一致且数据类型一致,在bean上添加autoWrite
ByType(按类型注入):
java类中引用数据的类型和spring容器(xml配置文件)中的class属性是同源关系的,这样的bean能够赋 值给引用数据类型
同源关系:
1、java类中引用数据的类型和中class值是一样的。
2、java类中引用数据的类型和中class值是父子类关系。
3、接口和对应的实现类
注意:xml配置文件中如果使用ByType作为为属性值的话,符合条件的同源bean只能有一个。
4)xml文件和注解对比优缺点:
xml优点:与源代码完全分离,就算日后业务需求发生改变,只需修改xml文件的的属性和配置,不需要修改源代码
用配置文件还可以创建别人提供的类对象
xml缺点:代码繁多,需要别写很多个配置。稍复杂。
需要翻阅配置文件才能知道属性的赋值和创建出来的对象的名字,不够清晰和直观
注解的优点:方便快捷,有提示。开发效率高
只需要观看源代码就可以知道相关的信息
注解的缺点:如果需求发生改变,需要频繁修改源代码。
源代码已修改,就需要重新编译,然后替换服务器中的class文件
在代码中加入多个注解,代码稍显杂乱,不美观
5)动态代理:
JDK的动态代理:要求是目标类必须实现接口
JAVA通过java.lang.reflect包提供的三个类支持代理模式:proxy、method 和 InvokeactionHandler
Proxy:通过proxy.newProxyInstance()创建目标对象
Method:调用method.invoke(taget ,args)执行目标方法
InvokeactionHandler接口:在接口的实现类中,重写invoke(),实现功能的增强
cglib的动态代理:
是第三方的工具库。能够生成代理的对象
cglib对象创建原理:继承。cglib能够创建目标类的子类,在子类中重写方法,实现功能的增强。
y" scope=“singleton”>
创建容器时,会创建所有单例对象
2、DI实现有两种语法:
1)、使用xml文件中的标签和属性,
2)、使用注解
Di的分类:
1)、设值注入
简单类型的设值注入:就是Spring调用实体类的set方法,完成赋值,最常用的方式
引用类型的设值注入:1)、ref作为属性 2)、ref作为子标签 。新建一个bean作为需要被赋值的 ref 内容
2)、构造注入
Spring去调用实体类的有参数构造方法,完成属性赋值。使用较少
简单类型的设值注入:
1、,每个
标签只能为一个属性赋值,在标签中生效。
如果需要同时给多个属性赋值,需要编写多个标签
2、使用xml注入赋值时,Spring关系的只有set方法。赋值时只要property中的属性能在实体类中找到对应的 set 方法,不管有没有sex这个成员变量(属性),不会违反Spring的约定。
3)、自动注入(autoWrite):
ByName(按属性名注入):
类中的属性名称与配置文件(xml)中bean标签的id一致且数据类型一致,在bean上添加autoWrite
ByType(按类型注入):
java类中引用数据的类型和spring容器(xml配置文件)中的class属性是同源关系的,这样的bean能够赋 值给引用数据类型
同源关系:
1、java类中引用数据的类型和中class值是一样的。
2、java类中引用数据的类型和中class值是父子类关系。
3、接口和对应的实现类
注意:xml配置文件中如果使用ByType作为为属性值的话,符合条件的同源bean只能有一个。
4)xml文件和注解对比优缺点:
xml优点:与源代码完全分离,就算日后业务需求发生改变,只需修改xml文件的的属性和配置,不需要修改源代码
用配置文件还可以创建别人提供的类对象
xml缺点:代码繁多,需要别写很多个配置。稍复杂。
需要翻阅配置文件才能知道属性的赋值和创建出来的对象的名字,不够清晰和直观
注解的优点:方便快捷,有提示。开发效率高
只需要观看源代码就可以知道相关的信息
注解的缺点:如果需求发生改变,需要频繁修改源代码。
源代码已修改,就需要重新编译,然后替换服务器中的class文件
在代码中加入多个注解,代码稍显杂乱,不美观
5)动态代理:
JDK的动态代理:要求是目标类必须实现接口
JAVA通过java.lang.reflect包提供的三个类支持代理模式:proxy、method 和 InvokeactionHandler
Proxy:通过proxy.newProxyInstance()创建目标对象
Method:调用method.invoke(taget ,args)执行目标方法
InvokeactionHandler接口:在接口的实现类中,重写invoke(),实现功能的增强
cglib的动态代理:
是第三方的工具库。能够生成代理的对象
cglib对象创建原理:继承。cglib能够创建目标类的子类,在子类中重写方法,实现功能的增强。
cglib的使用要求:目标类不能是final,目标方法不能是final