java高级特性编程及实战第三章课后,spring 学习笔记

爬山

复制代码

5、Map注入

复制代码

6、set注入

LOL

BOB

COC

复制代码

7、Null注入

复制代码

8、Properties注入

20190604

angus

复制代码

测试结果

image.png

6.3 拓展注入实现


User.java : 【注意:这里没有有参构造器!】

public class User {

private String name;

private int age;

public void setName(String name) {

this.name = name;

}

public void setAge(int age) {

this.age = age;

}

@Override

public String toString() {

return “User{” +

“name='” + name + ‘’’ +

“, age=” + age +

‘}’;

}

}

复制代码

1、P命名空间注入 : 需要在头文件中假如约束文件

导入约束 : xmlns:p=“http://www.springframework.org/schema/p”

复制代码

2、c 命名空间注入 : 需要在头文件中加入约束文件

导入约束 : xmlns:c=“http://www.springframework.org/schema/c”

复制代码

发现问题:爆红了,刚才我们没有写有参构造! 解决:把有参构造器加上,这里也能知道,c 就是所谓的构造器注入!

测试代码:

@Test

public void test02(){

ApplicationContext context = new

ClassPathXmlApplicationContext(“applicationContext.xml”);

User user = (User) context.getBean(“user”);

System.out.println(user);

}

复制代码

6.4 Bean的作用域


在Spring中,那些组成应用程序的主体及由Spring IoC容器所管理的对象,被称之为bean。简单地讲, bean就是由IoC容器初始化、装配及管理的对象 .

image.png

几种作用域中,request、session作用域仅在基于web的应用中使用(不必关心你所采用的是什么web 应用框架),只能用在基于web的Spring ApplicationContext环境。

6.4.1 Singleton

当一个bean的作用域为Singleton,那么Spring IoC容器中只会存在一个共享的bean实例,并且所有对 bean的请求,只要id与该bean定义相匹配,则只会返回bean的同一实例。Singleton是单例类型,就是 在创建起容器时就同时自动创建了一个bean的对象,不管你是否使用,他都存在了,每次获取到的对象 都是同一个对象。注意,Singleton作用域是Spring中的缺省作用域。要在XML中将bean定义成 singleton,可以这样配置:

复制代码

测试:

@Test

public void test03(){

ApplicationContext context = new ClassPathXmlApplicationContext(“applicationContext.xml”);

User user = (User) context.getBean(“user”);

User user2 = (User) context.getBean(“user”);

System.out.println(user==user2);

}

复制代码

6.4.2 Prototype

当一个bean的作用域为Prototype,表示一个bean定义对应多个对象实例。Prototype作用域的bean会 导致在每次对该bean请求(将其注入到另一个bean中,或者以程序的方式调用容器的getBean()方法) 时都会创建一个新的bean实例。Prototype是原型类型,它在我们创建容器的时候并没有实例化,而是 当我们获取bean的时候才会去创建一个对象,而且我们每次获取到的对象都不是同一个对象。根据经 验,对有状态的bean应该使用prototype作用域,而对无状态的bean则应该使用singleton作用域。在 XML中将bean定义成prototype,可以这样配置:

或者

复制代码

6.4.3 Request

当一个bean的作用域为Request,表示在一次HTTP请求中,一个bean定义对应一个实例;即每个HTTP 请求都会有各自的bean实例,它们依据某个bean定义创建而成。该作用域仅在基于web的Spring ApplicationContext情形下有效。考虑下面bean定义:

<bean id=“loginAction” class=cn.csdn.LoginAction" scope=“request”/>

复制代码

针对每次HTTP请求,Spring容器会根据loginAction bean的定义创建一个全新的LoginAction bean实 例,且该loginAction bean实例仅在当前HTTP request内有效,因此可以根据需要放心的更改所建实例 的内部状态,而其他请求中根据loginAction bean定义创建的实例,将不会看到这些特定于某个请求的 状态变化。当处理请求结束,request作用域的bean实例将被销毁。

6.4.4 Session

当一个bean的作用域为Session,表示在一个HTTP Session中,一个bean定义对应一个实例。该作用域 仅在基于web的Spring ApplicationContext情形下有效。考虑下面bean定义:

复制代码

针对某个HTTP Session,Spring容器会根据userPreferences bean定义创建一个全新的 userPreferences bean实例,且该userPreferences bean仅在当前HTTP Session内有效。与request作 用域一样,可以根据需要放心的更改所创建实例的内部状态,而别的HTTP Session中根据 userPreferences创建的实例,将不会看到这些特定于某个HTTP Session的状态变化。当HTTP Session 最终被废弃的时候,在该HTTP Session作用域内的bean也会被废弃掉。

7、Bean的自动装配

===========

  • 自动装配是使用spring满足bean依赖的一种方法

  • spring会在应用上下文中为某个bean寻找其依赖的bean。

Spring中bean有三种装配机制,分别是:

  1. 在xml中显式配置;

  2. 在java中显式配置;

  3. 隐式的bean发现机制和自动装配。

这里我们主要讲第三种:自动化的装配bean。

Spring的自动装配需要从两个角度来实现,或者说是两个操作:

  1. 组件扫描(component scanning):spring会自动发现应用上下文中所创建的bean;

  2. 自动装配(autowiring):spring自动满足bean之间的依赖,也就是我们说的IoC/DI;

组件扫描和自动装配组合发挥巨大威力,使的显示的配置降低到最少。

推荐 不使用自动装配xml配置 , 而是使用注解 .

7.1、测试环境搭建


  1. 新建一个项目

  2. 新建两个实体类,Cat Dog 都有一个叫的方法

public class Cat {

public void shout() {

System.out.println(“miao~”);

}

}

复制代码

public class Dog {

public void shout() {

System.out.println(“wang~”);

}

}

复制代码

  1. 新建一个用户类 User

public class User {

private Cat cat;

private Dog dog;

private String str;

}

复制代码

  1. 编写Spring配置文件
<?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">

复制代码

  1. 测试

public class MyTest {

@Test

public void testMethodAutowire() {

ApplicationContext context = new ClassPathXmlApplicationContext(“beans.xml”);

User user = (User) context.getBean(“user”);

user.getCat().shout();

user.getDog().shout();

}

}

复制代码

7.2、byName


autowire byName (按名称自动装配) 由于在手动配置xml过程中,常常发生字母缺漏和大小写等错误,而无法对其进行检查,使得开发效率 降低。 采用自动装配将避免这些错误,并且使配置简单化。

测试:

  1. 修改bean配置,增加一个属性 autowire=“byName”

复制代码

  1. 再次测试,结果依旧成功输出!

  2. 我们将 cat 的bean id修改为 catXXX

  3. 再次测试, 执行时报空指针java.lang.NullPointerException。因为按byName规则找不对应set方法,真正的setCat就没执行,对象就没有初始化,所以调用时就会报空指针错误。

小结: 当一个bean节点带有 autowire byName的属性时。

  1. 将查找其类中所有的set方法名,例如setCat,获得将set去掉并且首字母小写的字符串,即cat。

  2. 去spring容器中寻找是否有此字符串名称id的对象。

  3. 如果有,就取出注入;如果没有,就报空指针异常。

7.3、byType


autowire byType (按类型自动装配) 使用autowire byType首先需要保证:同一类型的对象,在spring容器中唯一。如果不唯一,会报不唯一 的异常。

NoUniqueBeanDefinitionException

复制代码

测试:

  1. 将user的bean配置修改一下 : autowire=“byType”

  2. 测试,正常输出

  3. 在注册一个cat 的bean对象!

复制代码

  1. 测试,报错:NoUniqueBeanDefinitionException

  2. 删掉cat2,将cat的bean名称改掉!测试!因为是按类型装配,所以并不会报异常,也不影响最后

的结果。甚至将id属性去掉,也不影响结果。

这就是按照类型自动装配!

7.4、使用注解


jdk1.5开始支持注解,spring2.5开始全面支持注解。

准备工作: 利用注解的方式注入属性。

  1. 在spring配置文件中引入context文件头

xmlns:context=“http://www.springframework.org/schema/context”

http://www.springframework.org/schema/context

http://www.springframework.org/schema/context/spring-context.xsd

复制代码

添加之后的结果

<?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

http://www.springframework.org/schema/context/spring-context.xsd"

context:annotation-config/

复制代码

  1. 开启属性注解支持!

context:annotation-config/

复制代码

7.4.1、@Autowired

  • @Autowired 是按类型自动转配的,不支持id匹配。

  • 需要导入 spring-aop的包!

测试:

  1. 将User类中的set方法去掉,使用@Autowired注解

public class User {

@Autowired

private Cat cat;

@Autowired

private Dog dog;

private String str;

public Cat getCat() {

return cat;

}

public Dog getDog() {

return dog;

}

public String getStr() {

return str;

}

}

复制代码

  1. 此时配置文件内容

context:annotation-config/

复制代码

  1. 测试,成功输出结果!

@Autowired(required=false) 说明: false,对象可以为null;true,对象必须存对象,不能为null。

//如果允许对象为null,设置required = false,默认为true

@Autowired(required = false)

private Cat cat;

复制代码

7.4.2、@Qualifier

  • @Autowired是根据类型自动装配的,加上@Qualifier则可以根据byName的方式自动装配

  • @Qualifier不能单独使用。

测试实验步骤:

  1. 配置文件修改内容,保证类型存在对象。且名字不为类的默认名字!

复制代码

  1. 没有加Qualifier测试,直接报错

  2. 在属性上添加Qualifier注解

@Autowired

@Qualifier(value = “cat2”)

private Cat cat;

@Autowired

@Qualifier(value = “dog2”)

private Dog dog;

复制代码

  1. 测试,成功输出!

7.4.3、@Resource

  • @Resource如有指定的name属性,先按该属性进行byName方式查找装配;

  • 其次再进行默认的byName方式进行装配;

  • 如果以上都不成功,则按byType的方式自动装配。

  • 都不成功,则报异常。

实体类:

public class User {

//如果允许对象为null,设置required = false,默认为true

@Resource(name = “cat2”)

private Cat cat;

@Resource

private Dog dog;

private String str;

}

复制代码

beans.xml

复制代码

测试:结果OK 配置文件2:beans.xml , 删掉cat2

复制代码

实体类上只保留注解

@Resource

private Cat cat;

@Resource

private Dog dog;

复制代码

结果:OK

结论:先进行byName查找,失败;再进行byType查找,成功。

7.5、小结


@Autowired与@Resource异同:

  1. @Autowired与@Resource都可以用来装配bean。都可以写在字段上,或写在setter方法上。

  2. @Autowired默认按类型装配(属于spring规范),默认情况下必须要求依赖对象必须存在,如果要允许null 值,可以设置它的required属性为false,如:@Autowired(required=false) ,如果我们想使用名称装配可以结合@Qualifier注解进行使用

  3. @Resource(属于J2EE复返),默认按照名称进行装配,名称可以通过name属性进行指定。如果没有指定name属性,当注解写在字段上时,默认取字段名进行按照名称查找,如果注解写在setter方法上默认取属性名进行装配。 当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。它们的作用相同都是用注解方式注入对象,但执行顺序不同。@Autowired先byType,@Resource先byName。

8、使用注解开发

========

8.1、说明


在spring4之后,想要使用注解形式,必须得要引入aop的包

image.png

在配置文件当中,还得要引入一个context约束

<?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">

复制代码

8.2、Bean的实现


我们之前都是使用 bean 的标签进行bean注入,但是实际开发中,我们一般都会使用注解!

  1. 配置扫描哪些包下的注解

<context:component-scan base-package=“com.angus.pojo”/>

复制代码

  1. 在指定包下编写类,增加注解

@Component(“user”)

// 相当于配置文件中

public class User {

public String name = “angus”;

}

复制代码

  1. 测试

@Test

public void test(){

ApplicationContext applicationContext =

new ClassPathXmlApplicationContext(“beans.xml”);

User user = (User) applicationContext.getBean(“user”);

System.out.println(user.name);

}

复制代码

8.3、属性注入


使用注解注入属性

  1. 可以不用提供set方法,直接在直接名上添加@value(“值”)

@Component(“user”)

// 相当于配置文件中

public class User {

@Value(“angus”)

// 相当于配置文件中

public String name;

}

复制代码

  1. 如果提供了set方法,在set方法上添加@value(“值”);

@Component(“user”)

public class User {

public String name;

@Value(“angus”)

public void setName(String name) {

this.name = name;

}

}

复制代码

8.4、衍生注解


我们这些注解,就是替代了在配置文件当中配置步骤而已!更加的方便快捷!

@Component三个衍生注解

为了更好的进行分层,Spring可以使用其它三个注解,功能一样,目前使用哪一个功能都一样。

  • @Controller:web层

  • @Service:service层

  • @Repository:dao层

写上这些注解,就相当于将这个类交给Spring管理装配了!

8.5、自动装配注解


在Bean的自动装配已经讲过了,可以回顾!

8.6、作用域


@scope

  • singleton:默认的,Spring会采用单例模式创建这个对象。关闭工厂 ,所有的对象都会销毁。

  • prototype:多例模式。关闭工厂 ,所有的对象不会销毁。内部的垃圾回收机制会回收

@Controller(“user”)

@Scope(“prototype”)

public class User {

@Value(“angus”)

public String name;

}

复制代码

8.7、小结


XML与注解比较

  • XML可以适用任何场景 ,结构清晰,维护方便

  • 注解不是自己提供的类使用不了,开发简单方便

xml与注解整合开发 :推荐最佳实践

  • xml管理Bean

  • 注解完成属性注入

  • 使用过程中, 可以不用扫描,扫描是为了类上的注解

作用:

  • 进行注解驱动注册,从而使注解生效

  • 用于激活那些已经在spring容器里注册过的bean上面的注解,也就是显示的向Spring注册

  • 如果不扫描包,就需要手动配置bean

  • 如果不加注解驱动,则注入的值为null!

8.8、基于Java类进行配置


JavaConfig 原来是 Spring 的一个子项目,它通过 Java 类的方式提供 Bean 的定义信息,在 Spring4 的 版本, JavaConfig 已正式成为 Spring4 的核心功能 。

测试:

  1. 编写一个实体类,Dog

@Component  //将这个类标注为Spring的一个组件,放到容器中!

public class Dog {

public String name = “dog”;

}

复制代码

  1. 新建一个config配置包,编写一个MyConfig配置类

@Configuration  //代表这是一个配置类

public class MyConfig {

@Bean //通过方法注册一个bean,这里的返回值就Bean的类型,方法名就是bean的id!

public Dog dog(){

return new Dog();

}

}

复制代码

  1. 测试

@Test

public void test2(){

ApplicationContext applicationContext =

new AnnotationConfigApplicationContext(MyConfig.class);

Dog dog = (Dog) applicationContext.getBean(“dog”);

System.out.println(dog.name);

}

复制代码

  1. 成功输出结果!

导入其他配置如何做呢?

  1. 我们再编写一个配置类!

@Configuration  //代表这是一个配置类

public class MyConfig2 {

}

复制代码

  1. 在之前的配置类中我们来选择导入这个配置类

@Configuration

@Import(MyConfig2.class)  //导入合并其他配置类,类似于配置文件中的 inculde 标签

public class MyConfig {

@Bean

public Dog dog(){

return new Dog();

}

}

复制代码

关于这种Java类的配置方式,我们在之后的SpringBoot 和 SpringCloud中还会大量看到,我们需要知道 这些注解的作用即可!

9、代理模式

======

为什么要学习代理模式,因为AOP的底层机制就是动态代理!

image.png

9.1、静态代理


静态代理角色分析

  • 抽象角色 : 一般使用接口或者抽象类来实现

  • 真实角色 : 被代理的角色

  • 代理角色 : 代理真实角色 ; 代理真实角色后 , 一般会做一些附属的操作 .

  • 客户 : 使用代理角色来进行一些操作 .

代码实现 Rent.java 即抽象角色

//抽象角色:租房

public interface Rent {

public void rent();

}

复制代码

Host.java 即真实角色

//真实角色: 房东,房东要出租房子

public class Host implements Rent{

public void rent() {

System.out.println(“房屋出租”);

}

}

复制代码

Proxy.java 即代理角色

//代理角色:中介

public class Proxy implements Rent {

private Host host;

public Proxy() { }

public Proxy(Host host) {

this.host = host;

}

//租房

public void rent(){

seeHouse();

host.rent();

fare();

}

//看房

public void seeHouse(){

System.out.println(“带房客看房”);

}

//收中介费

public void fare(){

System.out.println(“收中介费”);

}

}

复制代码

Client . java 即客户

//客户类,一般客户都会去找代理!

public class Client {

public static void main(String[] args) {

//房东要租房

Host host = new Host();

//中介帮助房东

Proxy proxy = new Proxy(host);

//你去找中介!

proxy.rent();

}

}

复制代码

分析: 在这个过程中,你直接接触的就是中介,就如同现实生活中的样子,你看不到房东,但是你依旧 租到了房东的房子通过代理,这就是所谓的代理模式,程序源自于生活,所以学编程的人,一般能够更 加抽象的看待生活中发生的事情。

9.2、静态代理的好处


  • 可以使得我们的真实角色更加纯粹 . 不再去关注一些公共的事情

  • 公共的业务由代理来完成,实现了业务的分工

  • 公共业务发生扩展时变得更加集中和方便

缺点 :

  • 类多了 , 多了代理类 , 工作量变大了 . 开发效率降低 .

我们想要静态代理的好处,又不想要静态代理的缺点,所以 , 就有了动态代理 !

9.3、静态代理再理解


同学们练习完毕后,我们再来举一个例子,巩固大家的学习! 练习步骤:

  1. 创建一个抽象角色,比如咋们平时做的用户业务,抽象起来就是增删改查!

//抽象角色:增删改查业务

public interface UserService {

void add();

void delete();

void update();

void query();

}

复制代码

  1. 我们需要一个真实对象来完成这些增删改查操作

//真实对象,完成增删改查操作的人

public class UserServiceImpl implements UserService {

public void add() {

System.out.println(“增加了一个用户”);

}

public void delete() {

System.out.println(“删除了一个用户”);

}

public void update() {

System.out.println(“更新了一个用户”);

}

public void query() {

System.out.println(“查询了一个用户”);

}

}

复制代码

  1. 需求来了,现在我们需要增加一个日志功能,怎么实现!
  • 思路1 :在实现类上增加代码 【麻烦!】

  • 思路2:使用代理来做,能够不改变原来的业务情况下,实现此功能就是最好的了!

  1. 设置一个代理类来处理日志! 代理角色

//代理角色,在这里面增加日志的实现

public class UserServiceProxy implements UserService {

private UserServiceImpl userService;

public void setUserService(UserServiceImpl userService) {

this.userService = userService;

}

public void add() {

log(“add”);

userService.add();

}

public void delete() {

log(“delete”);

userService.delete();

}

public void update() {

log(“update”);

userService.update();

}

public void query() {

log(“query”);

userService.query();

}

public void log(String msg){

System.out.println(“执行了”+msg+“方法”);

}

}

复制代码

  1. 测试访问类:

public class Client {

public static void main(String[] args) {

//真实业务

UserServiceImpl userService = new UserServiceImpl();

//代理类

UserServiceProxy proxy = new UserServiceProxy();

//使用代理类实现日志功能!

proxy.setUserService(userService);

proxy.add();

}

}

复制代码

OK,到了现在代理模式大家应该都没有什么问题了,重点大家需要理解其中的思想; 我们在不改变原来的代码的情况下,实现了对原有功能的增强,这是AOP中最核心的思想 【聊聊AOP:纵向开发,横向开发】

image.png

9.4、动态代理


  • 动态代理的角色和静态代理的一样.

  • 动态代理的代理类是动态生成的. 静态代理的代理类是我们提前写好的

  • 动态代理分为两类 : 一类是基于接口动态代理 , 一类是基于类的动态代理

  • 基于接口的动态代理----JDK动态代理

  • 基于类的动态代理–cglib

  • 现在用的比较多的是 javasist 来生成动态代理 . 百度一下javasist

  • 我们这里使用JDK的原生代码来实现,其余的道理都是一样的!

JDK的动态代理需要了解两个类 核心 : InvocationHandler 和 Proxy , 打开JDK帮助文档看看 【InvocationHandler:调用处理程序】

image.png

Object invoke(Object proxy, 方法 method, Object[] args);

//参数

//proxy - 调用该方法的代理实例

//method -所述方法对应于调用代理实例上的接口方法的实例。 方法对象的声明类将是该方法声明的接

// 口,它可以是代理类继承该方法的代理接口的超级接口。

//args -包含的方法调用传递代理实例的参数值的对象的阵列,或null如果接口方法没有参数。 原始

// 类型的参数包含在适当的原始包装器类的实例中,例如java.lang.Integer或java.lang.Boolean

复制代码

【Proxy : 代理】

image.png

image.png

image.png

//生成代理类

public Object getProxy(){

return Proxy.newProxyInstance(this.getClass().getClassLoader(),

rent.getClass().getInterfaces(),this);

}

复制代码

代码实现 抽象角色和真实角色和之前的一样! Rent.java 即抽象角色

//抽象角色:租房

public interface Rent {

public void rent();

}

复制代码

Host.java 即真实角色

//真实角色: 房东,房东要出租房子

public class Host implements Rent{

public void rent() {

System.out.println(“房屋出租”);

}

}

复制代码

ProxyInvocationHandler. java 即代理角色

public class ProxyInvocationHandler implements InvocationHandler {

private Rent rent;

public void setRent(Rent rent) {

this.rent = rent;

}

//生成代理类,重点是第二个参数,获取要代理的抽象角色!之前都是一个角色,现在可以代理一

类角色

public Object getProxy(){

return Proxy.newProxyInstance(this.getClass().getClassLoader(),

rent.getClass().getInterfaces(),this);

}

// proxy : 代理类 method : 代理类的调用处理程序的方法对象.

// 处理代理实例上的方法调用并返回结果

@Override

public Object invoke(Object proxy, Method method, Object[] args) throws

Throwable {

seeHouse();

//核心:本质利用反射实现!

Object result = method.invoke(rent, args);

fare();

return result;

}

//看房

public void seeHouse(){

System.out.println(“带房客看房”);

}

//收中介费

public void fare(){

System.out.println(“收中介费”);

}

}

复制代码

Client.java

//租客

public class Client {

public static void main(String[] args) {

//真实角色

Host host = new Host();

//代理实例的调用处理程序

ProxyInvocationHandler pih = new ProxyInvocationHandler();

pih.setRent(host); //将真实角色放置进去!

Rent proxy = (Rent)pih.getProxy(); //动态生成对应的代理类!

proxy.rent();

}

}

复制代码

核心:一个动态代理 , 一般代理某一类业务 , 一个动态代理可以代理多个类,代理的是接口!

9.5、深化理解


我们来使用动态代理实现代理我们后面写的UserService! 我们也可以编写一个通用的动态代理实现的类!所有的代理对象设置为Object即可!

public class ProxyInvocationHandler implements InvocationHandler {

private Object target;

public void setTarget(Object target) {

this.target = target;

}

//生成代理类

public Object getProxy(){

return Proxy.newProxyInstance(this.getClass().getClassLoader(),

target.getClass().getInterfaces(),this);

}

// proxy : 代理类

// method : 代理类的调用处理程序的方法对象.

public Object invoke(Object proxy, Method method, Object[] args) throws

Throwable {

log(method.getName());

Object result = method.invoke(target, args);

return result;

}

public void log(String methodName){

System.out.println(“执行了”+methodName+“方法”);

}

}

复制代码

测试!

public class Test {

public static void main(String[] args) {

//真实对象

UserServiceImpl userService = new UserServiceImpl();

//代理对象的调用处理程序

ProxyInvocationHandler pih = new ProxyInvocationHandler();

pih.setTarget(userService); //设置要代理的对象

UserService proxy = (UserService)pih.getProxy(); //动态生成代理类!

proxy.delete();

}

}

复制代码

【测试,增删改查,查看结果】

9.6、动态代理的好处


静态代理有的它都有,静态代理没有的,它也有!

  • 可以使得我们的真实角色更加纯粹 . 不再去关注一些公共的事情 .

  • 公共的业务由代理来完成 . 实现了业务的分工 ,

  • 公共业务发生扩展时变得更加集中和方便 .

  • 一个动态代理 , 一般代理某一类业务

  • 一个动态代理可以代理多个类,代理的是接口!

10、AOP

======

10.1 什么是AOP


AOP(Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理实现 程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的 一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使 得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

image.png

10.2 Aop在Spring中的作用


提供声明式事务;允许用户自定义切面

  • 横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要

  • 关注的部分,就是横切关注点。如日志 , 安全 , 缓存 , 事务等等 …

  • 切面(ASPECT):横切关注点 被模块化 的特殊对象。即,它是一个类。

  • 通知(Advice):切面必须要完成的工作。即,它是类中的一个方法。

  • 目标(Target):被通知对象。

  • 代理(Proxy):向目标对象应用通知之后创建的对象。

  • 切入点(PointCut):切面通知 执行的 “地点”的定义。

  • 连接点(JointPoint):与切入点匹配的执行点。

image.png

SpringAOP中,通过Advice定义横切逻辑,Spring中支持5种类型的Advice:

image.png

即 Aop 在 不改变原有代码的情况下, 去增加新的功能。

10.3 使用Spring实现Aop


【重点】使用AOP织入,需要导入一个依赖包!

org.aspectj

aspectjweaver

1.9.4

复制代码

第一种方式

通过 Spring API 实现 首先编写我们的业务接口和实现类

public interface UserService {

public void add();

public void delete();

public void update();

public void search();

}

复制代码

public class UserServiceImpl implements UserService{

@Override

public void add() {

System.out.println(“增加用户”);

}

@Override

public void delete() {

System.out.println(“删除用户”);

}

@Override

public void update() {

System.out.println(“更新用户”);

}

@Override

public void search() {

System.out.println(“查询用户”);

}

}

复制代码

然后去写我们的增强类 , 我们编写两个 , 一个前置增强 一个后置增强

public class Log implements MethodBeforeAdvice {

//method : 要执行的目标对象的方法

//objects : 被调用的方法的参数

//Object : 目标对象

@Override

public void before(Method method, Object[] objects, Object o) throws

Throwable {

System.out.println( o.getClass().getName() + “的” + method.getName()

  • “方法被执行了”);

}

}

复制代码

public class AfterLog implements AfterReturningAdvice {

//returnValue 返回值

//method被调用的方法

//args 被调用的方法的对象的参数

//target 被调用的目标对象

@Override

public void afterReturning(Object returnValue, Method method, Object[]

args, Object target) throws Throwable {

System.out.println(“执行了” + target.getClass().getName()

+“的”+method.getName()+“方法,”

+“返回值:”+returnValue);

}

}

复制代码

最后去spring的文件中注册 , 并实现aop切入实现 , 注意导入约束.

<?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">

aop:config

<aop:pointcut id=“pointcut” expression="execution(*

com.kuang.service.UserServiceImpl.*(…))"/>

<aop:advisor advice-ref=“log” pointcut-ref=“pointcut”/>

<aop:advisor advice-ref=“afterLog” pointcut-ref=“pointcut”/>

</aop:config>

复制代码

测试

public class MyTest {

@Test

public void test(){

ApplicationContext context = new

ClassPathXmlApplicationContext(“beans.xml”);

UserService userService = (UserService)

context.getBean(“userService”);

userService.search();

}

}

复制代码

Aop的重要性 : 很重要。 一定要理解其中的思路 , 主要是思想的理解这一块 .

Spring的Aop就是将公共的业务 (日志 , 安全等) 和领域业务结合起来 , 当执行领域业务时 , 将会把公共业 务加进来 . 实现公共业务的重复利用 . 领域业务更纯粹 , 程序猿专注领域业务 , 其本质还是动态代理.

第二种方式

自定义类来实现Aop

目标业务类不变依旧是userServiceImpl

第一步 : 写我们自己的一个切入类

public class DiyPointcut {

public void before(){

System.out.println(“---------方法执行前---------”);

}

public void after(){

System.out.println(“---------方法执行后---------”);

}

}

复制代码

去spring中配置

aop:config

<aop:aspect ref=“diy”>

<aop:pointcut id=“diyPonitcut” expression="execution(*

com.kuang.service.UserServiceImpl.*(…))"/>

<aop:before pointcut-ref=“diyPonitcut” method=“before”/>

<aop:after pointcut-ref=“diyPonitcut” method=“after”/>

</aop:aspect>

</aop:config>

复制代码

测试:

public class MyTest {

@Test

public void test(){

ApplicationContext context = new

ClassPathXmlApplicationContext(“beans.xml”);

UserService userService = (UserService)

context.getBean(“userService”);

userService.add();

}

}

复制代码

第三种方式

使用注解实现

第一步:编写一个注解实现的增强类

package com.angus.config;

import org.aspectj.lang.ProceedingJoinPoint;

import org.aspectj.lang.annotation.After;

import org.aspectj.lang.annotation.Around;

import org.aspectj.lang.annotation.Aspect;

import org.aspectj.lang.annotation.Before;

@Aspect

public class AnnotationPointcut {

@Before(“execution(* com.angus.service.UserServiceImpl.*(…))”)

public void before(){

System.out.println(“---------方法执行前---------”);

}

@After(“execution(* com.angus.service.UserServiceImpl.*(…))”)

public void after(){

System.out.println(“---------方法执行后---------”);

}

@Around(“execution(* com.angus.service.UserServiceImpl.*(…))”)

public void around(ProceedingJoinPoint jp) throws Throwable {

System.out.println(“环绕前”);

System.out.println(“签名:”+jp.getSignature());

//执行目标方法proceed

Object proceed = jp.proceed();

System.out.println(“环绕后”);

System.out.println(proceed);

}

}

复制代码

第二步:在Spring配置文件中,注册bean,并增加支持注解的配置

aop:aspectj-autoproxy/

复制代码

aop:aspectj-autoproxy:说明

通过aop命名空间的<aop:aspectj-autoproxy />声明自动为spring容器中那些配置@aspectJ切面

的bean创建代理,织入切面。当然,spring 在内部依旧采用AnnotationAwareAspectJAutoProxyCreator进行自动代理的创建工作,但具体实现的细节已经被

<aop:aspectj-autoproxy />隐藏起来了。

<aop:aspectj-autoproxy />有一个proxy-target-class属性,默认为false,表示使用jdk动态

代理织入增强,当配为<aop:aspectj-autoproxy  poxy-target-class=“true”/>时,表示使用

CGLib动态代理技术织入增强。不过即使proxy-target-class设置为false,如果目标类没有声明接

口,则spring将自动使用CGLib动态代理。

复制代码

11、整合Mybatis

============

步骤:

  1. 导入相关jar包

  2. junit

junit

junit

4.12

复制代码

  1. mybatis

org.mybatis

mybatis

3.5.2

复制代码

  1. mysql-connector-java

mysql

mysql-connector-java

5.1.47

复制代码

  1. spring相关

org.springframework

spring-webmvc

5.1.10.RELEASE

org.springframework

spring-jdbc

5.1.10.RELEASE

复制代码

  1. aspectJ AOP 织入器

org.aspectj

aspectjweaver

1.9.4

复制代码

  1. mybatis-spring整合包 【重点】

org.mybatis

mybatis-spring

2.0.2

复制代码

  1. 配置Maven静态资源过滤问题!

src/main/java

**/*.properties

**/*.xml

true

复制代码

  1. 编写配置文件

  2. 代码实现

回忆MyBatis


编写pojo实体类

package com.kuang.pojo;

public class User {

private int id;  //id

private String name;  //姓名

private String pwd;  //密码

}

复制代码

实现mybatis的配置文件

<?xml version="1.0" encoding="UTF-8" ?>

<property name=“url”

value="jdbc:mysql://localhost:3306/mybatis?

useSSL=true&useUnicode=true&characterEncoding=utf8"/>

复制代码

UserDao接口编写

public interface UserMapper {

public List selectUser();

}

复制代码

接口对应的Mapper映射文件

<?xml version="1.0" encoding="UTF-8" ?>

select * from user

复制代码

测试类

@Test

public void selectUser() throws IOException {

String resource = “mybatis-config.xml”;

InputStream inputStream = Resources.getResourceAsStream(resource);

SqlSessionFactory sqlSessionFactory = new

SqlSessionFactoryBuilder().build(inputStream);

SqlSession sqlSession = sqlSessionFactory.openSession();

UserMapper mapper = sqlSession.getMapper(UserMapper.class);

List userList = mapper.selectUser();

for (User user: userList){

System.out.println(user);

}

sqlSession.close();

}

复制代码

MyBatis-Spring学习


引入Spring之前需要了解mybatis-spring包中的一些重要类; www.mybatis.org/spring/zh/i…

image.png

什么是 MyBatis-Spring?

MyBatis-Spring 会帮助你将 MyBatis 代码无缝地整合到 Spring 中。

知识基础 在开始使用 MyBatis-Spring 之前,你需要先熟悉 Spring 和 MyBatis 这两个框架和有关它们的术语。这 很重要。

MyBatis-Spring 需要以下版本:

image.png

如果使用 Maven 作为构建工具,仅需要在 pom.xml 中加入以下代码即可:

org.mybatis

mybatis-spring

2.0.2

复制代码

要和 Spring 一起使用 MyBatis,需要在 Spring 应用上下文中定义至少两样东西:一个 SqlSessionFactory 和至少一个数据映射器类。 在 MyBatis-Spring 中,可使用 SqlSessionFactoryBean 来创建 SqlSessionFactory 。 要配置 这个工厂 bean,只需要把下面代码放在 Spring 的 XML 配置文件中:

<bean id=“sqlSessionFactory”

class=“org.mybatis.spring.SqlSessionFactoryBean”>

复制代码

注意: SqlSessionFactory 需要一个 DataSource (数据源)。 这可以是任意的 DataSource ,只需要和配置其它 Spring 数据库连接一样配置它就可以了。 在基础的 MyBatis 用法中,是通过 SqlSessionFactoryBuilder 来创建 SqlSessionFactory 的。 而在 MyBatis-Spring 中,则使用 SqlSessionFactoryBean 来创建。

在 MyBatis 中,你可以使用 SqlSessionFactory 来创建 SqlSession 。一旦你获得一个 session 之后,你可以使用它来执行映射了的语句,提交或回滚连接,最后,当不再需要它的时候,你 可以关闭 session。

SqlSessionFactory 有一个唯一的必要属性:用于 JDBC 的 DataSource 。这可以是任意的 DataSource 对象,它的配置方法和其它 Spring 数据库连接是一样的。

一个常用的属性是 configLocation ,它用来指定 MyBatis 的 XML 配置文件路径。它在需要修改 MyBatis 的基础配置非常有用。通常,基础配置指的是 < settings>< typeAliases> 元素。 需要注意的是,这个配置文件并不需要 是一个完整的 MyBatis 配置。确切地说,任何环境配置 ( < environments> ),数据源( < DataSource> )和 MyBatis 的事务管理器 ( < transactionManager> )都会被忽略SqlSessionFactoryBean 会创建它自有的 MyBatis 环境配置( Environment ),并按要求设置自定义环境的值。

SqlSessionTemplate 是 MyBatis-Spring 的核心。作为 SqlSession 的一个实现,这意味着可 以使用它无缝代替你代码中已经在使用的 SqlSession

模板可以参与到 Spring 的事务管理中,并且由于其是线程安全的,可以供多个映射器类使用,你应该总 是用 SqlSessionTemplate 来替换 MyBatis 默认的 DefaultSqlSession 实现。在同一应用程 序中的不同类之间混杂使用可能会引起数据一致性的问题。 可以使用 SqlSessionFactory 作为构造方法的参数来创建 SqlSessionTemplate 对象。

复制代码

现在,这个 bean 就可以直接注入到你的 DAO bean 中了。你需要在你的 bean 中添加一个 SqlSession 属性,就像下面这样:

public class UserDaoImpl implements UserDao {

private SqlSession sqlSession;

public void setSqlSession(SqlSession sqlSession) {

this.sqlSession = sqlSession;

}

public User getUser(String userId) {

return sqlSession.getMapper…;

}

}

复制代码

按下面这样,注入 SqlSessionTemplate

复制代码

整合实现一


  1. 引入Spring配置文件beans.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">

复制代码

  1. 配置数据源替换mybaits的数据源

<bean id=“dataSource”

class=“org.springframework.jdbc.datasource.DriverManagerDataSource”>

<property name=“url” value="jdbc:mysql://localhost:3306/mybatis?

useSSL=true&useUnicode=true&characterEncoding=utf8"/>

复制代码

  1. 配置SqlSessionFactory,关联MyBatis

<bean id=“sqlSessionFactory”

class=“org.mybatis.spring.SqlSessionFactoryBean”>

<property name=“configLocation” value="classpath:mybatis-

config.xml"/>

<property name=“mapperLocations”

value=“classpath:com/kuang/dao/*.xml”/>

复制代码

  1. 注册sqlSessionTemplate,关联sqlSessionFactory;

复制代码

  1. 增加Dao接口的实现类;私有化sqlSessionTemplate

public class UserDaoImpl implements UserMapper {

//sqlSession不用我们自己创建了,Spring来管理

private SqlSessionTemplate sqlSession;

public void setSqlSession(SqlSessionTemplate sqlSession) {

this.sqlSession = sqlSession;

}

public List selectUser() {

UserMapper mapper = sqlSession.getMapper(UserMapper.class);

return mapper.selectUser();

}

}

复制代码

  1. 注册bean实现

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

最后如何让自己一步步成为技术专家

说句实话,如果一个打工人不想提升自己,那便没有工作的意义,毕竟大家也没有到养老的年龄。

当你的技术在一步步贴近阿里p7水平的时候,毫无疑问你的薪资肯定会涨,同时你能学到更多更深的技术,交结到更厉害的大牛。

推荐一份Java架构之路必备的学习笔记,内容相当全面!!!

成年人的世界没有容易二字,前段时间刷抖音看到一个程序员连着加班两星期到半夜2点的视频。在这个行业若想要拿高薪除了提高硬实力别无他法。

你知道吗?现在有的应届生实习薪资都已经赶超开发5年的程序员了,实习薪资26K,30K,你没有紧迫感吗?做了这么多年还不如一个应届生,真的非常尴尬!

进了这个行业就不要把没时间学习当借口,这个行业就是要不断学习,不然就只能被裁员。所以,抓紧时间投资自己,多学点技术,眼前困难,往后轻松!

【关注】+【转发】+【点赞】支持我!创作不易!
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

复制代码

按下面这样,注入 SqlSessionTemplate

复制代码

整合实现一


  1. 引入Spring配置文件beans.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">

复制代码

  1. 配置数据源替换mybaits的数据源

<bean id=“dataSource”

class=“org.springframework.jdbc.datasource.DriverManagerDataSource”>

<property name=“url” value="jdbc:mysql://localhost:3306/mybatis?

useSSL=true&useUnicode=true&characterEncoding=utf8"/>

复制代码

  1. 配置SqlSessionFactory,关联MyBatis

<bean id=“sqlSessionFactory”

class=“org.mybatis.spring.SqlSessionFactoryBean”>

<property name=“configLocation” value="classpath:mybatis-

config.xml"/>

<property name=“mapperLocations”

value=“classpath:com/kuang/dao/*.xml”/>

复制代码

  1. 注册sqlSessionTemplate,关联sqlSessionFactory;

复制代码

  1. 增加Dao接口的实现类;私有化sqlSessionTemplate

public class UserDaoImpl implements UserMapper {

//sqlSession不用我们自己创建了,Spring来管理

private SqlSessionTemplate sqlSession;

public void setSqlSession(SqlSessionTemplate sqlSession) {

this.sqlSession = sqlSession;

}

public List selectUser() {

UserMapper mapper = sqlSession.getMapper(UserMapper.class);

return mapper.selectUser();

}

}

复制代码

  1. 注册bean实现

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。[外链图片转存中…(img-ZargJD8j-1713456736721)]

[外链图片转存中…(img-S8PmugLX-1713456736724)]

[外链图片转存中…(img-kpP3yXU8-1713456736726)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

最后如何让自己一步步成为技术专家

说句实话,如果一个打工人不想提升自己,那便没有工作的意义,毕竟大家也没有到养老的年龄。

当你的技术在一步步贴近阿里p7水平的时候,毫无疑问你的薪资肯定会涨,同时你能学到更多更深的技术,交结到更厉害的大牛。

推荐一份Java架构之路必备的学习笔记,内容相当全面!!!

[外链图片转存中…(img-rLT1CY5T-1713456736727)]

成年人的世界没有容易二字,前段时间刷抖音看到一个程序员连着加班两星期到半夜2点的视频。在这个行业若想要拿高薪除了提高硬实力别无他法。

你知道吗?现在有的应届生实习薪资都已经赶超开发5年的程序员了,实习薪资26K,30K,你没有紧迫感吗?做了这么多年还不如一个应届生,真的非常尴尬!

进了这个行业就不要把没时间学习当借口,这个行业就是要不断学习,不然就只能被裁员。所以,抓紧时间投资自己,多学点技术,眼前困难,往后轻松!

【关注】+【转发】+【点赞】支持我!创作不易!
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 16
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值