核心内容


概念
Spring几个重要概念
1.Spring可以整合其他的框架(解读: Spring是管理框架的框架)
2.Spring有两个核心的概念: IOC 和 AOP
3.IOC [Inversion Of Control反转控制]
传统的开发模式[JdbcUtils/反射]程序------>环境//程序读取环境配置,然后自己创建对象.


解读


配置Been.xml文件

public class SpringBeenstest {
@Test
public void getMonster() {
// 创建容器
ApplicationContext ioc =
new ClassPathXmlApplicationContext("beans.xml");
Object monster01 = ioc.getBean("monster01");
System.out.println(monster01);
}
}<?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 class="com.zfc.been.Monster" id="monster01">
<property name="Monsterid" value="100"/>
<property name="name" value="黑悟空"/>
<property name="skill" value="喷火"/>
</bean>
</beans>
手动实现 Spring底层
package com.zfc.spring.hspapplicationcontext;
import com.hspedu.spring.bean.Monster;
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 zfc
* @version 1.0
* 解读
* 1. 这个程序用于实现Spring的一个简单容器机制
* 2. 后面我们还会详细的实现
* 3. 这里我们实现如何将beans.xml文件进行解析,并生成对象,放入容器中
* 4. 提供一个方法 getBean(id) 返回对应的对象
* 5. 这里就是一个开胃小点心, 理解Spring容器的机制
*/
public class ZfcApplicationContext {
private ConcurrentHashMap<String, Object> singletonObjects =
new ConcurrentHashMap<>();
//构造器
//接收一个容器的配置文件 比如 beans.xml, 该文件默认在src
public ZfcApplicationContext(String iocBeanXmlFile) throws Exception {
//1. 得到类加载路径
String path = this.getClass().getResource("/").getPath();
//2. 创建 Saxreader
SAXReader saxReader = new SAXReader();
//3. 得到Document对象
Document document =
saxReader.read(new File(path + iocBeanXmlFile));
//4. 得到rootDocument
Element rootElement = document.getRootElement();
//5. 得到第一个bean-monster01
Element bean = (Element) rootElement.elements("bean").get(0);
//6. 获取到第一个bean-monster01的相关属性
String id = bean.attributeValue("id");
String classFullPath = 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");
//7. 使用反射创建对象.=> 回顾反射机制
Class<?> aClass = Class.forName(classFullPath);
//这里o对象就是Monster对象
Monster o = (Monster) aClass.newInstance();
//给o对象赋值
//反射来赋值=> 这里老师就简化,直接赋值->目的就是先理解流程
//这里的方法就是setter方法
//Method[] declaredMethods = aClass.getDeclaredMethods();
//for (Method declaredMethod : declaredMethods) {
// declaredMethod.invoke();
//}
//赋值
o.setMonsterId(monsterId);
o.setName(name);
o.setSkill(skill);
//8. 将创建好的对象放入到singletonObjects
singletonObjects.put(id, o);
}
public Object getBean(String id) {
//这里小伙伴可以在处理
return singletonObjects.get(id);
}
}
package com.zfc.spring.hspapplicationcontext;
import com.Zfc.spring.bean.Monster;
/**
* @author zfc
* @version 1.0
*/
public class ZfcApplicationContextTest {
public static void main(String[] args) throws Exception {
ZfcApplicationContext ioc =
new ZfcApplicationContext("beans.xml");
Monster monster01 = (Monster)ioc.getBean("monster01");
System.out.println("monter01=" + monster01);
System.out.println("monster01.name=" + monster01.getName());
System.out.println("ok");
}
}
Spring 配置/管理bean
按照类型来获取 直接传入bean.class对象

通过构造器配置Bean


可以通过类型来配置

通过P名称空间来分配Bean对象


通过ref来实现


直接配置内部Bean对象

通过list配置

通过Map对象赋值

用Set属性赋值

通过Array赋值

通过Properties进行配置

通过utils名称空间来创建List集合

属性级联赋值


通过静态工厂获取



通过实例工厂获取



通过FactoryBean获取Bean



Bean配置信息重用

Bean创建顺序

Bean单例和多实例

细节

Bean的生命周期

Bean的后置处理器(重要!!!)

package com.zfc.spring.bean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
/**
* @author zfc
* @version 1.0
* 这是一个后置处理器, 需要实现 BeanPostProcessor接口
*/
public class MyBeanPostProcessor implements BeanPostProcessor {
/**
* 什么时候被调用: 在Bean的init方法前被调用
* @param bean 传入的在IOC容器中创建/配置Bean
* @param beanName 传入的在IOC容器中创建/配置Bean的id
* @return Object 程序员对传入的bean 进行修改/处理【如果有需要的话】 ,返回
* @throws BeansException
*/
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessBeforeInitialization().. bean="
+ bean + " beanName=" + beanName);
//初步体验案例: 如果类型是House的统一改成 上海豪宅
//对多个对象进行处理/编程==>切面编程
if(bean instanceof House) {
((House)bean).setName("上海豪宅~");
}
return null;
}
/**
* 什么时候被调用: 在Bean的init方法后被调用
* @param bean 传入的在IOC容器中创建/配置Bean
* @param beanName 传入的在IOC容器中创建/配置Bean的id
* @return 程序员对传入的bean 进行修改/处理【如果有需要的话】 ,返回
* @throws BeansException
*/
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessAfterInitialization().. bean="
+ bean + " beanName=" + beanName);
return bean;
}
}
AOP(Aspect-Oriented Programming,面向切面编程)和OOP(Object-Oriented Programming,面向对象编程)是两种不同的编程范式,它们在设计和实现软件系统时具有不同的侧重点和应用场景。
OOP(面向对象编程)
面向对象编程是一种编程范式,通过将数据和行为封装在对象中来组织代码。OOP的核心概念包括:
1.对象:对象是类的实例,包含数据(属性)和方法(行为)。
2.类:类是对象的蓝图,定义了对象的属性和方法。
3.继承:允许一个类继承另一个类的属性和方法,促进代码重用。
4.多态:同一方法可以在不同对象上表现出不同的行为,增强代码的灵活性。
5.封装:将对象的内部状态与外部世界隔离,只允许通过公共方法访问和修改对象的状态。
AOP(面向切面编程)
面向切面编程是一种编程范式,旨在通过将横切关注点(cross-cutting concerns)从业务逻辑中分离出来来改善代码的模块化。AOP的核心概念包括:
6.切面(Aspect):定义横切关注点的模块,可以在不同的地方应用,例如日志、安全、事务管理等。
7.连接点(Join Point):程序执行的特定点,比如方法调用、对象实例化等,切面可以在这些点上进行操作。
8.通知(Advice):在连接点上执行的代码,可以在连接点之前、之后或异常时执行。
9.切入点(Pointcut):定义哪些连接点会被切面应用,可以通过表达式指定。
10.织入(Weaving):将切面与主程序代码结合的过程,可以在编译时、类加载时或运行时进行。
AOP 和 OOP 的区别
| 特性 | OOP | AOP |
|-----------------------|-----------------------------------------|---------------------------------------- - |
| 关注点 | 主要关注对象及其行为 | 主要关注横切关注点(如日志、安全等) |
| 模块化方式 | 通过类和对象封装 | 通过切面分离横切关注点 |
| 代码重用 | 通过继承和组合 | 通过切面复用逻辑 |
| 设计目的 | 封装数据和行为,促进重用和维护 | 解耦关注点,增强模块化和可维护性 |
| 处理方式 | 在类内部定义行为 | 在运行时或编译时动态插入行为 |
何时使用 AOP?
11.跨切关注点:需要对日志、安全、事务、缓存等逻辑进行统一管理。
12.代码复用:希望在多个模块中复用相同的逻辑,而不需要在每个模块中重复代码。
13.降低耦合度:希望将横切关注点与核心业务逻辑分离,从而提高系统的可维护性。
结合使用
AOP 和 OOP 是互补的,通常在实际项目中可以结合使用。OOP 适合实现核心业务逻辑,而 AOP 可以帮助处理与业务逻辑无关但又重要的关注点。通过这样的结合,开发者能够编写更加清晰、可维护和可扩展的代码。
通过属性文件配置Bean


自动装配



基于注解配置Bean(重点!!!!!!)


注意事项和细节



在Spring框架中,@Controller、@Service和@Component都是用来标注类的注解,它们都属于Spring的依赖注入(DI)和控制反转(IoC)机制的一部分。虽然它们都可以用来将类注册为Spring容器中的Bean,但它们有不同的语义和用途。以下是它们的主要区别:
1. @Controller
1.用途:
2.@Controller用于标注控制层的组件,通常用于处理HTTP请求和响应。
3.它是Spring MVC框架的一部分,主要用于Web应用程序。
4.特点:
5.用于定义控制器类,这些类处理用户的请求并返回视图(例如,HTML页面)。
6.结合@RequestMapping等注解来处理特定的URL请求。
7.示例:
@Controller
public class MyController {
@RequestMapping("/hello")
public String sayHello() {
return "hello"; // 返回视图名称
}
}2. @Service
8.用途:
9.@Service用于标注服务层的组件,通常用于实现业务逻辑。
10.特点:
11.它标识的是服务类,这些类包含核心业务逻辑,通常与数据访问层(Repository)交互。
12.Spring会处理这些类的事务管理(Transaction Management)等。
13.示例:
@Service
public class UserService {
public User getUserById(Long id) {
// 业务逻辑
}
}3. @Component
14.用途:
15.@Component是一个通用的组件注解,用于标识一个类是一个Spring管理的Bean。
16.特点:
17.可以用于任何类,它是最基本的构造,除了特殊的功能注解(如@Controller和@Service)之外的所有组件都可以使用它。
18.适用于不符合其他类型(如Controller或Service)的任何类。
19.示例:
@Component
public class UtilityComponent {
public void doSomething() {
// 工具类功能
}
}总结
20.职责:
21.@Controller: 处理HTTP请求,返回视图。
22.@Service: 实现业务逻辑,处理与数据层的交互。
23.@Component: 用于任何普通的Spring Bean。
24.使用场景:
25.使用@Controller来定义Web控制器,使用@Service来定义业务逻辑,使用@Component来标识任何普通组件。
选择建议
通常情况下,建议在合适的层使用相应的注解,以提高代码的可读性和可维护性。即使@Service和@Component的功能相似,使用特定的注解可以帮助其他开发者快速理解类的意图和用途。
手动实现IOC容器
package com.zfcedu.spring.annotation;
import com.zfcedu.spring.zfcapplicationcontext.zfcApplicationContext;
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 java.io.File;
import java.lang.annotation.Annotation;
import java.net.URL;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author zfc
* @version 1.0
* zfcSpringApplicationContext 类的作用类似Spring原生ioc容器
*/
public class zfcSpringApplicationContext {
private Class configClass;
//ioc我存放的就是通过反射创建的对象(基于注解方式)
private final ConcurrentHashMap<String, Object> ioc =
new ConcurrentHashMap<>();
//构造器
public zfcSpringApplicationContext(Class configClass) {
this.configClass = configClass;
//System.out.println("this.configClass=" + this.configClass);
//获取要扫描的包
//1. 先得到zfcSpringConfig配置的的@ComponentScan(value = "com.zfcedu.spring.component")
ComponentScan componentScan =
(ComponentScan) this.configClass.getDeclaredAnnotation(ComponentScan.class);
//2. 通过componentScan的value=> 即要扫描的包
String path = componentScan.value();
System.out.println("要扫描的包= " + path);
//得到要扫描的包下的所有资源(类 .class)
//1.得到类的加载器
ClassLoader classLoader =
zfcApplicationContext.class.getClassLoader();
//2. 通过类的加载器获取到要扫描的包的资源 url=》类似一个路径
path = path.replace(".", "/");//一定要把. 替换成 /
URL resource =
classLoader.getResource(path);
System.out.println("resource=" + resource);
//3. 将要加载的资源(.class) 路径下的文件进行遍历=>io
File file = new File(resource.getFile());
if (file.isDirectory()) {
File[] files = file.listFiles();
for (File f : files) {
System.out.println("=====================");
System.out.println("=" + f.getAbsolutePath());
//D:\zfcedu_spring\spring\out\production\spring\com\zfcedu\spring\component\UserService.class
//获取到 com.zfcedu.spring.component.UserService
String fileAbsolutePath = f.getAbsolutePath();
//这里我们只处理.class文件
if (fileAbsolutePath.endsWith(".class")) {
//1. 获取到类名
String className =
fileAbsolutePath.substring(fileAbsolutePath.lastIndexOf("\\") + 1, fileAbsolutePath.indexOf(".class"));
//System.out.println("className=" + className);
//2. 获取类的完整的路径(全类名)
//老师解读 path.replace("/",".") => com.zfcedu.spring.component.
String classFullName = path.replace("/", ".") + "." + className;
//System.out.println("classFullName=" + classFullName);
//3. 判断该类是不是需要注入容器, 就看该类是不是有注解 @Component @Service..
try {
//这时,我们就得到该类的Class对象
//Class clazz = Class.forName(classFullName)
//说一下
//1. Class clazz = Class.forName(classFullName) 可以反射加载类
//2. classLoader.loadClass(classFullName); 可以反射类的Class
//3. 区别是 : 上面方式后调用来类的静态方法, 下面方法不会
//4. aClass.isAnnotationPresent(Component.class) 判断该类是否有 @Component
Class<?> aClass = classLoader.loadClass(classFullName);
if (aClass.isAnnotationPresent(Component.class) ||
aClass.isAnnotationPresent(Controller.class) ||
aClass.isAnnotationPresent(Service.class) ||
aClass.isAnnotationPresent(Repository.class)) {
//这里老师演示一个Component注解指定value,分配id
//老师就是演示了一下机制.
if(aClass.isAnnotationPresent(Component.class)) {
//获取到该注解
Component component = aClass.getDeclaredAnnotation(Component.class);
String id = component.value();
if(!"".endsWith(id)) {
className = id;//替换
}
}
//这时就可以反射对象,并放入到容器中
Class<?> clazz = Class.forName(classFullName);
Object instance = clazz.newInstance();
//放入到容器中, 将类名的首字母小写作为id
//StringUtils
ioc.put(StringUtils.uncapitalize(className) , instance);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
//编写方法返回对容器中对象
public Object getBean(String name) {
return ioc.get(name);
}
}
package com.zfc.Annotation.componet;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
import java.io.File;
import java.net.URL;
import java.util.Arrays;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author zfc
* @version 1.0
*/
@SuppressWarnings({"all"})
public class ZfcSpringApplicationContext {
// 定义一个类属性
private Class configClass;
// 创建一个concurrentHashMap来存放注解信息
private static ConcurrentHashMap<String, Object> ioc = new ConcurrentHashMap<>();
// 构造器
public ZfcSpringApplicationContext(Class configClass) {
this.configClass = configClass;
// 获取要扫描的包
ComponentScan componentScan =
(ComponentScan) this.configClass.getDeclaredAnnotation(ComponentScan.class);
System.out.println("componentScan = " + componentScan);
String[] path = componentScan.value();
System.out.println("要扫描的包=" + Arrays.toString(path));
// 要扫描的包=[com.zfc.Annotation.componet]
// 先得到类加载器
ClassLoader classLoader = ZfcSpringApplicationContext.class.getClassLoader();
// 扫描包下面的类
for (String p : path) {
String packagePath = p.replace(".", "/");
System.out.println("packagePath:" + packagePath);
// packagePath:com/zfc/Annotation/componet
// 扫描包下面的类
URL resource = classLoader.getResource(packagePath);
System.out.println("resource=" + resource);
// resource=file:/D:/javacode/mavenproject/maven_java/spring/target/classes/com/zfc/Annotation/componet
File file = new File(resource.getFile());
if(file.isDirectory()) {
File[] files = file.listFiles();
for (File f : files) {
System.out.println("=====================");
String fileAbsolutePath = f.getAbsolutePath();
if (fileAbsolutePath.endsWith(".class")) {
// 获取到类名
String ClassName = fileAbsolutePath.substring(fileAbsolutePath.lastIndexOf("\\") + 1,
fileAbsolutePath.indexOf(".class"));
System.out.println("ClassName=" + ClassName);
String classFullName = p.replace("/", ".") + "." + ClassName;
System.out.println("classFullName=" + classFullName);
// System.out.println("=" + f.getAbsolutePath());
// =====================
// ClassName=User
// classFullName=com.zfc.Annotation.componet.User
// =====================
// ClassName=UserAction
// classFullName=com.zfc.Annotation.componet.UserAction
// =====================
// ClassName=UserDAO
// classFullName=com.zfc.Annotation.componet.UserDAO
// =====================
// ClassName=UserServet
// classFullName=com.zfc.Annotation.componet.UserServet
// =====================
// ClassName=ZfcSpringApplicationContext
// classFullName=com.zfc.Annotation.componet.ZfcSpringApplicationContext
// =====================
// ClassName=ZfcSpringConfig
// classFullName=com.zfc.Annotation.componet.ZfcSpringConfig
// 反射获取类对象
try {
Class<?> aClass = classLoader.loadClass(classFullName);
if(aClass.isAnnotationPresent(Component.class) ||
aClass.isAnnotationPresent(Controller.class) ||
aClass.isAnnotationPresent(Service.class) ||
aClass.isAnnotationPresent(Repository.class)
){
// 保存到IOC容器中
ioc.put(ClassName, aClass.newInstance());
};
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}
}
}
public Object getBean(String name) {
return ioc.get(name);
}
}
package com.zfc.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author zfc
* @version 1.0
* 这是一个扫描类注解
* * 1. @Target(ElementType.TYPE)指定我们的ComponentScan注解可以修饰 Type程序元素
* * 2. @Retention(RetentionPolicy.RUNTIME) 指定ComponentScan注解 保留范围
* * 3. String value() default ""; 表示ComponentScan 可以传入 value
*/
@SuppressWarnings({"all"})
// 添加自定义注解的可作用的范围
@Target(value = {ElementType.TYPE})
@Retention(value = RetentionPolicy.RUNTIME)
public @interface ComponetScan {
String value() default "";
}
package com.zfc.Annotation.componet;
import com.zfc.Annotation.ComponetScan;
import org.springframework.context.annotation.ComponentScan;
/**
* @author zfc
* @version 1.0
*/
@ComponentScan(value = "com.zfc.Annotation.componet")
public class ZfcSpringConfig {
// 该类用于识别一个注解
}
基于注解自动装配bean


泛型依赖注入
基本说明
1.为了更好的管理有继承和相互依赖的bean的自动装配,spring还提供基于泛型依赖的注入机制
2.在继承关系复杂情况下,泛型依赖注入就会有很大的优越性

AOP

横切关注点




注意事项

动态代理

手动实现AOP
package com.zfc.spring.proxy2;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* @author zfc
* @version 1.0
* VehicleProxyProvider 该类可以返回一个代理对象.
*/
public class VehicleProxyProvider {
//定义一个属性
//target_vehicle 表示真正要执行的对象
//该对象实现了Vehicle接口
private Vehicle target_vehicle;
//构造器
public VehicleProxyProvider(Vehicle target_vehicle) {
this.target_vehicle = target_vehicle;
}
//编写一个方法,可以返回一个代理对象, 该代理对象可以通过反射机制调用到被代理对象的方法
//解读
//1. 这个方法非常重要, 理解有一定难度
public Vehicle getProxy() {
//得到类加载器
ClassLoader classLoader =
target_vehicle.getClass().getClassLoader();
//得到要代理的对象/被执行对象 的接口信息,底层是通过接口来完成调用
Class<?>[] interfaces = target_vehicle.getClass().getInterfaces();
//创建InvocationHandler 对象
//因为 InvocationHandler 是接口,所以我们可以通过匿名对象的方式来创建该对象
/**
*
* public interface InvocationHandler {
* public Object invoke(Object proxy, Method method, Object[] args)
* throws Throwable;
* }
* invoke 方法是将来执行我们的target_vehicle的方法时,会调用到
*
*/
InvocationHandler invocationHandler = new InvocationHandler() {
/**
* invoke 方法是将来执行我们的target_vehicle的方法时,会调用到
* @param o 表示代理对象
* @param method 就是通过代理对象调用方法时,的哪个方法 代理对象.run()
* @param args : 表示调用 代理对象.run(xx) 传入的参数
* @return 表示 代理对象.run(xx) 执行后的结果.
* @throws Throwable
*/
@Override
public Object invoke(Object o, Method method, Object[] args)
throws Throwable {
System.out.println("交通工具开始运行了....");
//这里是我们的反射基础 => OOP
//method 是?: public abstract void com.zfc.spring.proxy2.Vehicle.run()
//target_vehicle 是? Ship对象
//args 是null
//这里通过反射+动态绑定机制,就会执行到被代理对象的方法
//执行完毕就返回
Object result = method.invoke(target_vehicle, args);
System.out.println("交通工具停止运行了....");
return result;
}
};
/*
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
解读
1. Proxy.newProxyInstance() 可以返回一个代理对象
2. ClassLoader loader: 类的加载器.
3. Class<?>[] interfaces 就是将来要代理的对象的接口信息
4. InvocationHandler h 调用处理器/对象 有一个非常重要的方法invoke
*/
Vehicle proxy =
(Vehicle)Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
return proxy;
}
}
Test
package com.zfc.spring.proxy2;
import org.junit.jupiter.api.Test;
/**
* @author zfc
* @version 1.0
*/
public class TestVehicle {
@Test
public void run() {
//OOP基础=>java基础
Vehicle vehicle = new Ship();
//动态绑定
vehicle.run();
}
@Test
public void proxyRun() {
//创建Ship对象
Vehicle vehicle = new Car();
//创建VehicleProxyProvider对象, 并且我们传入的要代理的对象
VehicleProxyProvider vehicleProxyProvider =
new VehicleProxyProvider(vehicle);
//获取代理对象, 该对象可以代理执行方法
//解读
//1. porxy 编译类型 Vehicle
//2. 运行类型 是代理类型 class com.sun.proxy.$Proxy9
Vehicle proxy = vehicleProxyProvider.getProxy();
System.out.println("proxy的编译类型是 Vehicle");
System.out.println("proxy的运行类型是 " + proxy.getClass());
//下面就要给大家解读/debug怎么 执行到 代理对象的 public Object invoke(Object o, Method method, Object[] args)
//梳理完毕. proxy的编译类型是 Vehicle, 运行类型是 class com.sun.proxy.$Proxy9
//所以当执行run方法时,会执行到 代理对象的invoke
//如何体现动态 [1. 被代理的对象 2. 方法]
//proxy.run();
String result = proxy.fly(10000);
System.out.println("result=" + result);
}
}
切入点表达式的具体使用


注意细节

1.为什么要使用动态代理?
动态代理:在不改变原有代码的情况下上进行对象功能增强 使用代理对象代替原来的对象完成功能 进而达到拓展功能的目
的
2.JDK Proxy 动态优理面向接口的动态代理 :特点:
1.一定要有接口和实现类的存在 代理对象增强的是实现类 在实现接口的方法重写的方法
2.生成的代理对象只能转换成 接口的不能转换成 被代理类
3.代理对象只能增强接口中定义的方法 实现类中其他和接口无关的方法是无法增强的
4.代理对象只能读取到接口中方法上的注解 不能读取到实现类方法上的注解使用方法:
cglib动态代理模式是面向父类
特点:
1.面向父类的和接口没有直接关系
2.不仅可以增强接口中定义的方法还可以增强其他方法
3.可以读取父类中方法上的所有注解
两个动态代理的区别
1.JDK动态代理是面向接口的,只能增强实现类中接口中存在的方法。CGlib是面向父类的,可以增强父类的所有方法2.JDK得到的对象是JDK代理对象实例,而CGlib得到的对象是被代理对象的子类
JoinPoint
//后置通知
@After(value = "execution(public void Car.run())")
public void ok4(JoinPoint joinPoint) {
Signature signature = joinPoint.getSignature();
System.out.println("切面类的ok4()-执行的目标方法-" + signature.getName());
//演示一下JoinPoint常用的方法.
joinPoint.getSignature().getName(); // 获取目标方法名
joinPoint.getSignature().getDeclaringType().getSimpleName(); // 获取目标方法所属类的简单类名
joinPoint.getSignature().getDeclaringTypeName(); // 获取目标方法所属类的类名
joinPoint.getSignature().getModifiers(); // 获取目标方法声明类型(public、private、protected)
Object[] args = joinPoint.getArgs(); // 获取传入目标方法的参数,返回一个数组
joinPoint.getTarget(); // 获取被代理的对象
joinPoint.getThis(); // 获取代理对象自己
}拿到返回的结果
@AfterReturning(value = "myPointCut()", returning = "res")
public void showSuccessEndLog(JoinPoint joinPoint, Object res) {
Signature signature = joinPoint.getSignature();
System.out.println("SmartAnimalAspect-切面类showSuccessEndLog()-方法执行正常结束-日志-方法名-" + signature.getName() + " 返回的结果是=" + res);
}捕获异常信息
//异常通知:即把showExceptionLog方法切入到目标对象方法执行发生异常的的catch{}
//@AfterThrowing(value = "execution(public float com.hspedu.spring.aop.aspectj.SmartDog.getSum(float, float))", throwing = "throwable")
//直接使用切入点表达式
@AfterThrowing(value = "myPointCut()", throwing = "throwable")
public void showExceptionLog(JoinPoint joinPoint, Throwable throwable) {
Signature signature = joinPoint.getSignature();
System.out.println("SmartAnimalAspect-切面类showExceptionLog()-方法执行异常-日志-方法名-" + signature.getName() + " 异常信息=" + throwable);获取最终通知
//最终通知:即把showFinallyEndLog方法切入到目标方法执行后(不管是否发生异常,都要执行 finally{})
//@After(value = "execution(public float com.hspedu.spring.aop.aspectj.SmartDog.getSum(float, float))")
//直接使用切入点
@After(value = "myPointCut()")
public void showFinallyEndLog(JoinPoint joinPoint) {
Signature signature = joinPoint.getSignature();
System.out.println("SmartAnimalAspect-切面类showFinallyEndLog()-方法最终执行完毕-日志-方法名-" + signature.getName());环绕通知
package com.zfc.spring.aop.aspectj;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
/**
* @author zfc
* @version 1.0
* 切面类 , 类似于我们以前自己写的MyProxyProvider,但是功能强大很多
*/
//@Aspect //表示是一个切面类[底层切面编程的支撑(动态代理+反射+动态绑定...)]
//@Component //会注入SmartAnimalAspect2到容器
public class SmartAnimalAspect2 {
//演示环绕通知的使用-了解
//老师解读
//1. @Around: 表示这是一个环绕通知[完成其它四个通知的功能]
//2. value = "execution(public float com.hspedu.spring.aop.aspectj.SmartDog.getSum(float, float)) 切入点表达式
//3. doAround 表示要切入的方法 - 调用结构 try-catch-finally
@Around(value = "execution(public float com.hspedu.spring.aop.aspectj.SmartDog.getSum(float, float))")
public Object doAround(ProceedingJoinPoint joinPoint) {
Object result = null;
String methodName = joinPoint.getSignature().getName();
try {
//1.相当于前置通知完成的事情
Object[] args = joinPoint.getArgs();
List<Object> argList = Arrays.asList(args);
System.out.println("AOP环绕通知[-前置通知]" + methodName + "方法开始了--参数有:" + argList);
//在环绕通知中一定要调用joinPoint.proceed()来执行目标方法
result = joinPoint.proceed();
//2.相当于返回通知完成的事情
System.out.println("AOP环绕通知[-返回通知]" + methodName + "方法结束了--结果是:" + result);
} catch (Throwable throwable) {
//3.相当于异常通知完成的事情
System.out.println("AOP环绕通知[-异常通知]" + methodName + "方法抛异常了--异常对象:" + throwable);
} finally {
//4.相当于最终通知完成的事情
System.out.println("AOP环绕通知[-后置通知]" + methodName + "方法最终结束了...");
}
return result;
}
}
切入点表达式重用
package com.zfc.spring.aop.aspectj;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Arrays;
/**
* @author zfc
* @version 1.0
* 切面类 , 类似于我们以前自己写的MyProxyProvider,但是功能强大很多
*
*/
@Order(value = 2)//表示该切面类执行的顺序, value的值越小, 优先级越高
@Aspect //表示是一个切面类[底层切面编程的支撑(动态代理+反射+动态绑定...)]
@Component //会注入SmartAnimalAspect到容器
public class SmartAnimalAspect {
//定义一个切入点, 在后面使用时可以直接引用, 提高了复用性
@Pointcut(value = "execution(public float com.zfc.spring.aop.aspectj.SmartDog.getSum(float, float)))")
public void myPointCut() {
}
//希望将f1方法切入到SmartDog-getSum前执行-前置通知
/**
* 解读
* 1. @Before 表示前置通知:即在我们的目标对象执行方法前执行
* 2. value = "execution(public float com.hspedu.spring.aop.aspectj.SmartDog.getSum(float, float)
* 指定切入到哪个类的哪个方法 形式是: 访问修饰符 返回类型 全类名.方法名(形参列表)
* 3. showBeginLog方法可以理解成就是一个切入方法, 这个方法名是可以程序员指定 比如:showBeginLog
* 4. JoinPoint joinPoint 在底层执行时,由AspectJ切面框架, 会给该切入方法传入 joinPoint对象
* , 通过该方法,程序员可以获取到 相关信息
*
* @param joinPoint
*/
//@Before(value = "execution(public float com.hspedu.spring.aop.aspectj.SmartDog.getSum(float, float))")
//这里我们使用定义好的切入点
@Before(value = "myPointCut()")
public void showBeginLog(JoinPoint joinPoint) {
//通过连接点对象joinPoint 可以获取方法签名
Signature signature = joinPoint.getSignature();
System.out.println("SmartAnimalAspect-切面类showBeginLog()[使用的myPointCut()]-方法执行前-日志-方法名-" + signature.getName() + "-参数 "
+ Arrays.asList(joinPoint.getArgs()));
}
//返回通知:即把showSuccessEndLog方法切入到目标对象方法正常执行完毕后的地方
//解读
//1. 如果我们希望把目标方法执行的结果,返回给切入方法
//2. 可以再 @AfterReturning 增加属性 , 比如 returning = "res"
//3. 同时在切入方法增加 Object res
//4. 注意: returning = "res" 和 Object res 的 res名字一致
//@AfterReturning(value = "execution(public float com.hspedu.spring.aop.aspectj.SmartDog.getSum(float, float))", returning = "res")
//使用切入点
@AfterReturning(value = "myPointCut()", returning = "res")
public void showSuccessEndLog(JoinPoint joinPoint, Object res) {
Signature signature = joinPoint.getSignature();
System.out.println("SmartAnimalAspect-切面类showSuccessEndLog()-方法执行正常结束-日志-方法名-" + signature.getName() + " 返回的结果是=" + res);
}
//异常通知:即把showExceptionLog方法切入到目标对象方法执行发生异常的的catch{}
//@AfterThrowing(value = "execution(public float com.hspedu.spring.aop.aspectj.SmartDog.getSum(float, float))", throwing = "throwable")
//直接使用切入点表达式
@AfterThrowing(value = "myPointCut()", throwing = "throwable")
public void showExceptionLog(JoinPoint joinPoint, Throwable throwable) {
Signature signature = joinPoint.getSignature();
System.out.println("SmartAnimalAspect-切面类showExceptionLog()-方法执行异常-日志-方法名-" + signature.getName() + " 异常信息=" + throwable);
}
//最终通知:即把showFinallyEndLog方法切入到目标方法执行后(不管是否发生异常,都要执行 finally{})
//@After(value = "execution(public float com.hspedu.spring.aop.aspectj.SmartDog.getSum(float, float))")
//直接使用切入点
@After(value = "myPointCut()")
public void showFinallyEndLog(JoinPoint joinPoint) {
Signature signature = joinPoint.getSignature();
System.out.println("SmartAnimalAspect-切面类showFinallyEndLog()-方法最终执行完毕-日志-方法名-" + signature.getName());
}
//新的前置通知
//@Before(value = "execution(public void com.hspedu.spring.aop.aspectj.Phone.work()) || execution(public void com.hspedu.spring.aop.aspectj.Camera.work())")
//public void hi(JoinPoint joinPoint) {
// Signature signature = joinPoint.getSignature();
// System.out.println("切面类的hi()-执行的目标方法-" + signature.getName());
//}
//切入表达式也可以指向接口的方法, 这时切入表达式会对实现了接口的类/对象生效
//比如下面我们是对UsbInterface 切入,那么对实现类Phone 和 Camera对象都作用了
@Before(value = "execution(public void com.hspedu.spring.aop.aspectj.UsbInterface.work())")
public void hi(JoinPoint joinPoint) {
Signature signature = joinPoint.getSignature();
System.out.println("切面类的hi()-执行的目标方法-" + signature.getName());
}
//给Car配置一个前置通知
@Before(value = "execution(public void Car.run())")
public void ok1(JoinPoint joinPoint) {
Signature signature = joinPoint.getSignature();
System.out.println("切面类的ok1()-执行的目标方法-" + signature.getName());
}
//返回通知
@AfterReturning(value = "execution(public void Car.run())")
public void ok2(JoinPoint joinPoint) {
Signature signature = joinPoint.getSignature();
System.out.println("切面类的ok2()-执行的目标方法-" + signature.getName());
}
//异常通知
@AfterThrowing(value = "execution(public void Car.run())")
public void ok3(JoinPoint joinPoint) {
Signature signature = joinPoint.getSignature();
System.out.println("切面类的ok3()-执行的目标方法-" + signature.getName());
}
//后置通知
@After(value = "execution(public void Car.run())")
public void ok4(JoinPoint joinPoint) {
Signature signature = joinPoint.getSignature();
System.out.println("切面类的ok4()-执行的目标方法-" + signature.getName());
//演示一下JoinPoint常用的方法.
joinPoint.getSignature().getName(); // 获取目标方法名
joinPoint.getSignature().getDeclaringType().getSimpleName(); // 获取目标方法所属类的简单类名
joinPoint.getSignature().getDeclaringTypeName(); // 获取目标方法所属类的类名
joinPoint.getSignature().getModifiers(); // 获取目标方法声明类型(public、private、protected)
Object[] args = joinPoint.getArgs(); // 获取传入目标方法的参数,返回一个数组
joinPoint.getTarget(); // 获取被代理的对象
joinPoint.getThis(); // 获取代理对象自己
}
}
切面类执行的顺序
切面优先级问题:
如果同一个方法,有多个切面在同一个切入点切入,那么执行的优先级如何控制
基本语法:
@order(value=n)来控制 n值越小,优先级越高
1.不能理解成:优先级高的每个消息通知都先执行,这个和方法调用机制(和Filter过滤器链式调用类似)

基于XML配置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:context="http://www.springframework.org/schema/context"
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/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--使用XML配置,完成AOP编程-->
<!--配置一个切面类对象-bean-->
<bean class="com.zfc.spring.aop.xml.SmartAnimalAspect" id="smartAnimalAspect"/>
<!--配置一个SmartDog对象-bean-->
<bean class="com.hspedu.spring.aop.xml.SmartDog" id="smartDog"/>
<!--配置切面类, 细节一定要引入 xmlns:aop-->
<aop:config>
<!--配置切入点-->
<aop:pointcut id="myPointCut" expression="execution(public float com.zfc.spring.aop.xml.SmartDog.getSum(float, float)))"/>
<!--配置切面的前置,返回, 异常, 最终通知-->
<aop:aspect ref="smartAnimalAspect" 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>AOP与BeanPostProcessor的关系

1)AOP底层是基于BeanPostProcessor 机制的.
2)即在Bean创建好后,根据是否需要AOP处理,决定返代理对象,还是原生Bean
3)在返回代理对象时,就可以根据要代理的类和方法来返回
4)其实这个机制并不难,本质就是在BeanPostProcessor 机制+动态代理技术

拓展类加载器
java的类加载器3种
1.Bootstrap类加载器 对应路径jre/libI
2.APP类加载器 对应路径classpath
3.Ext 类加载器 对应路径jre/lib/ext
注解的简单实现
package com.zfc.spring;
import com.hspedu.spring.annotation.AfterReturning;
import com.hspedu.spring.annotation.Before;
import com.hspedu.spring.component.SmartAnimalAspect;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* @author zfc
* @version 1.0
*/
public class zfcTest {
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
//1. 获取SmartAnimalAspect的class对象
Class<SmartAnimalAspect> smartAnimalAspectClass = SmartAnimalAspect.class;
//2. 遍历该类的所有方法
for (Method declaredMethod : smartAnimalAspectClass.getDeclaredMethods()) {
//如果切面类的方法有Before注解
if (declaredMethod.isAnnotationPresent(Before.class)) {
//得到切面类的切入方法名
System.out.println("m:= " + declaredMethod.getName());
//得到Before(value="xxxx")
//得到Before注解
Before annotation = declaredMethod.getAnnotation(Before.class);
//得到Before注解的value
System.out.println("value:= " + annotation.value());
//得到切入要执行的方法.[反射基础]
Method declaredMethod1 = smartAnimalAspectClass.getDeclaredMethod(declaredMethod.getName());
//调用切入方法[通过反射调用]
declaredMethod1.invoke(smartAnimalAspectClass.newInstance(), null);
} else if (declaredMethod.isAnnotationPresent(AfterReturning.class)) {
//如果发现切面类有AfterReturning注解,同样可以进行处理..
System.out.println("m:= " + declaredMethod.getName());
AfterReturning annotation = declaredMethod.getAnnotation(AfterReturning.class);
System.out.println("value:= " + annotation.value());
//得到切入要执行的方法.
Method declaredMethod1 = smartAnimalAspectClass.getDeclaredMethod(declaredMethod.getName());
//调用切入方法[反射调用]
declaredMethod1.invoke(smartAnimalAspectClass.newInstance(), null);
}
}
}
}
Jdbc Tmpelate
实际需求:如果程序员就希望使用 spring 框架来做项目,spring 框架如何处理对数据库的操作呢?
1.方案1.使用前面做项目开发的 JdbcUtils 类
2.方案2,其实spring提供了一个操作数据库(表)功能强大的类JdbcTemplate 。我们可以同ioc容器来配置一个JdbcTemplate对象,使用它来完成对数据库表的各种操作.
<?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 https://www.springframework.org/schema/context/spring-context.xsd">
<!--配置要扫描包-->
<context:component-scan
base-package="com.zfc.spring.jdbctemplate.dao"/>
<!--引入外部的jdbc.properties文件-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!--配置数据源对象-DataSoruce-->
<bean class="com.mchange.v2.c3p0.ComboPooledDataSource" id="dataSource">
<!--给数据源对象配置属性值-->
<property name="user" value="${jdbc.user}"/>
<property name="password" value="${jdbc.pwd}"/>
<property name="driverClass" value="${jdbc.driver}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
</bean>
<!--配置JdbcTemplate对象-->
<bean class="org.springframework.jdbc.core.JdbcTemplate" id="jdbcTemplate">
<!--给JdbcTemplate对象配置dataSource-->
<property name="dataSource" ref="dataSource"/>
</bean>
<!--配置NamedParameterJdbcTemplate对象-->
<bean class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate"
id="namedParameterJdbcTemplate">
<!--通过构造器,设置数据源-->
<constructor-arg name="dataSource" ref="dataSource"/>
</bean>
</beans>增删改查
package com.zfc.spring.test;
import com.zfc.spring.bean.Monster;
import com.zfc.spring.jdbctemplate.dao.MonsterDao;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.transaction.annotation.Transactional;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author zfc
* @version 1.0
*/
public class JdbcTemplateTest {
@Test
public void testDatasourceByJdbcTemplate() throws SQLException {
//获取到容器
ApplicationContext ioc =
new ClassPathXmlApplicationContext("JdbcTemplate_ioc.xml");
DataSource dataSource = ioc.getBean(DataSource.class);
Connection connection = dataSource.getConnection();
System.out.println("获取到connection= " + connection);
connection.close();
System.out.println("ok");
}
//测试通过JdbcTemplate对象完成添加数据
@Test
public void addDataByJdbcTemplate() {
//获取到容器
ApplicationContext ioc =
new ClassPathXmlApplicationContext("JdbcTemplate_ioc.xml");
//获取JdbcTemplate对象
JdbcTemplate jdbcTemplate = ioc.getBean(JdbcTemplate.class);
//1. 添加方式1
//String sql = "INSERT INTO monster VALUES(400, '红孩儿', '枪法')";
//jdbcTemplate.execute(sql);
//2. 添加方式2
String sql = "INSERT INTO monster VALUES(?, ?, ?)";
//affected表示 执行后表受影响的记录数
int affected = jdbcTemplate.update(sql, 500, "红孩儿2", "枪法2");
System.out.println("add ok affected=" + affected);
}
//测试通过JdbcTemplate对象完成修改数据
@Test
public void updateDataByJdbcTemplate() {
//获取到容器
ApplicationContext ioc =
new ClassPathXmlApplicationContext("JdbcTemplate_ioc.xml");
//获取JdbcTemplate对象
JdbcTemplate jdbcTemplate = ioc.getBean(JdbcTemplate.class);
//组织SQL
String sql = "UPDATE monster SET skill=? WHERE id=?";
int affected = jdbcTemplate.update(sql, "美女计", 300);
System.out.println("update ok affected= " + affected);
}
//批量添加二个monster 白蛇精和青蛇精
//这里有一个使用API的技巧
/**
* 说明
* 1. 对于某个类, 有很多API, 使用的步骤
* 2. 老韩的使用技巧(1) 先确定API名字 (2) 根据API提供相应的参数 [组织参数]
* (3) 把自己的调用思路清晰 (4) 根据API, 可以推测类似的用法和功能
*/
/**
* batch add data
* 批量添加二个monster 白蛇精和青蛇精-update(sql,List<Object[]>)
*/
@Test
public void addBatchDataByJdbcTemplate() {
ApplicationContext ioc = new ClassPathXmlApplicationContext("JdbcTemplate_ioc.xml");
//得到JdbcTemplate bean
JdbcTemplate jdbcTemplate = ioc.getBean(JdbcTemplate.class);//添加..
//1. 先确定,猜测API名称 batchUpdate[如果出现问题,才重新玩]
//public int[] batchUpdate(String sql, List<Object[]> batchArgs){}
//2. 准备参数
String sql = "INSERT INTO monster VALUES(?, ?, ?)";
List<Object[]> batchArgs = new ArrayList<>();
batchArgs.add(new Object[]{600, "老鼠精", "偷吃粮食"});
batchArgs.add(new Object[]{700, "老猫精", "抓老鼠"});
//3. 调用
//说明:返回结果是一个数组,每个元素对应上面的sql语句对表的影响记录数
int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs);
//输出
for (int anInt : ints) {
System.out.println("anInt=" + anInt);
}
System.out.println("batch add ok..");
}
//查询id=100的monster并封装到Monster实体对象[在实际开发中,非常有用]
@Test
public void selectDataByJdbcTemplate() {
ApplicationContext ioc = new ClassPathXmlApplicationContext("JdbcTemplate_ioc.xml");
//得到JdbcTemplate bean
JdbcTemplate jdbcTemplate = ioc.getBean(JdbcTemplate.class);
//组织SQL
//通过BeanPropertyRowMapper获取rowmapper 是一个接口,可以将查询的结果,封装到你指定的Monster对象中.
//1. 确定API : queryForObject()
//public <T> T queryForObject(String sql, RowMapper<T> rowMapper, @Nullable Object... args)
//2.准备参数
String sql = "SELECT id AS monsterId, NAME, skill FROM monster WHERE id = 100";
//使用RowMapper 接口来对返回的数据,进行一个封装-》底层使用的反射->setter
//这里有一个细节: 你查询的记录的表的字段需要和 Monster的对象字段名保持一致
RowMapper<Monster> rowMapper = new BeanPropertyRowMapper<>(Monster.class);
//jdbcTemplate
Monster monster = jdbcTemplate.queryForObject(sql, rowMapper);
System.out.println("monster= " + monster);
System.out.println("查询ok");
}
//查询id>=200的monster并封装到Monster实体对象
/**
* 查询多条记录
*/
@Test
public void selectMulDataByJdbcTemplate() {
ApplicationContext ioc =
new ClassPathXmlApplicationContext("JdbcTemplate_ioc.xml");
//得到JdbcTemplate bean
JdbcTemplate jdbcTemplate = ioc.getBean(JdbcTemplate.class);
//组织SQL
//通过BeanPropertyRowMapper获取rowmapper 是一个接口,可以将查询的结果,封装到你指定的Monster对象中.
//1. 确定API
//public <T> T query(String sql, RowMapper<T> rowMapper, Object... args){}
//2. 组织参数
String sql = "SELECT id AS monsterId, NAME, skill FROM monster WHERE id >= ?";
RowMapper<Monster> rowMapper = new BeanPropertyRowMapper<>(Monster.class);
//3. 调用
List<Monster> monsterList = jdbcTemplate.query(sql, rowMapper, 100);
for (Monster monster : monsterList) {
System.out.println("monster= " + monster);
}
}
//查询返回结果只有一行一列的值,比如查询id=100的怪物名
/**
* 查询返回结果只有一行一列的值
*/
@Test
public void selectScalarByJdbcTemplate() {
ApplicationContext ioc =
new ClassPathXmlApplicationContext("JdbcTemplate_ioc.xml");
//得到JdbcTemplate bean
JdbcTemplate jdbcTemplate = ioc.getBean(JdbcTemplate.class);
//1. 确定API
//public <T> T queryForObject(String sql, Class<T> requiredType)
//2. 提供参数
String sql = "SELECT NAME FROM monster WHERE id = 100";
//Class<T> requiredType 表示你返回的单行单列的数据类型
String name =
jdbcTemplate.queryForObject(sql, String.class);
System.out.println("返回name= " + name);
}
//使用Map传入具名参数完成操作,比如添加 螃蟹精.:name 就是具名参数形式需要使用NamedParameterJdbcTemplate 类, 语句形式: String sql = "INSERT INTO monster VALUES(:my_id, :name, :skill)";
/**
* 使用Map传入具名参数完成操作,比如添加
*/
@Test
public void testDataByNamedParameterJdbcTemplate() {
ApplicationContext ioc =
new ClassPathXmlApplicationContext("JdbcTemplate_ioc.xml");
//得到NamedParameterJdbcTemplate bean
NamedParameterJdbcTemplate namedParameterJdbcTemplate =
ioc.getBean(NamedParameterJdbcTemplate.class);
//1. 确定使用API
//public int update(String sql, Map<String, ?> paramMap)
//2. 准备参数 [:my_id, :name, :skill] 要求按照规定的名字来设置参数
String sql = "INSERT INTO monster VALUES(:id, :name, :skill)";
Map<String, Object> paramMap = new HashMap<>();
//给paramMap填写数据
paramMap.put("id", 800);
paramMap.put("name", "蚂蚁精");
paramMap.put("skill", "喜欢打洞");
//3. 调用
int affected = namedParameterJdbcTemplate.update(sql, paramMap);
System.out.println("add ok affected=" + affected);
}
//使用sqlparametersoruce 来封装具名参数,还是添加一个Monster 狐狸精
@Test
public void operDataBySqlparametersoruce() {
ApplicationContext ioc =
new ClassPathXmlApplicationContext("JdbcTemplate_ioc.xml");
//得到NamedParameterJdbcTemplate bean
NamedParameterJdbcTemplate namedParameterJdbcTemplate =
ioc.getBean(NamedParameterJdbcTemplate.class);
//确定API
//public int update(String sql, SqlParameterSource paramSource)
//public BeanPropertySqlParameterSource(Object object)
//准备参数
String sql = "INSERT INTO monster VALUES(:monsterId, :name, :skill)";
Monster monster = new Monster(900, "大象精", "搬运木头");
SqlParameterSource sqlParameterSource =
new BeanPropertySqlParameterSource(monster);
//调用
int affected =
namedParameterJdbcTemplate.update(sql, sqlParameterSource);
System.out.println("add ok affected= " + affected);
}
//测试MonsterDAO
@Test
public void monsterDaoSave() {
ApplicationContext ioc =
new ClassPathXmlApplicationContext("JdbcTemplate_ioc.xml");
MonsterDao monsterDao = ioc.getBean(MonsterDao.class);
Monster monster = new Monster(1000, "小鸭精", "吃鱼");
monsterDao.save(monster);
System.out.println("MonsterDAO保存 ok ..");
}
}
声明式事务
需求说明-用户购买商品
我们需要去处理用户购买商品的业务逻辑:分析:当一个用户要去购买商品应该包含三个步骤:
1.通过商品id获取价格.
2.购买商品(某人购买商品,修改用户的余额.)
3.修改库存量
4.其实大家可以看到,这时,我们需要涉及到三张表商品表,用户表,商品存量表。 应该使用事务处理
@Transactional
使用该注解,可以进行声明式事务控制
事务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:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--配置要扫描的包-->
<context:component-scan base-package="com.zfc.spring.tx.dao"/>
<context:component-scan base-package="com.zfc.spring.tx.service"/>
<!--引入外部的jdbc.properties文件-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!--配置数据源对象-DataSoruce-->
<bean class="com.mchange.v2.c3p0.ComboPooledDataSource" id="dataSource">
<!--给数据源对象配置属性值-->
<property name="user" value="${jdbc.user}"/>
<property name="password" value="${jdbc.pwd}"/>
<property name="driverClass" value="${jdbc.driver}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
</bean>
<!--配置JdbcTemplate对象-->
<bean class="org.springframework.jdbc.core.JdbcTemplate" id="jdbcTemplate">
<!--给JdbcTemplate对象配置dataSource-->
<property name="dataSource" ref="dataSource"/>
</bean>
<!--配置事务管理器-对象
1. DataSourceTransactionManager 这个对象是进行事务管理-debug源码
2. 一定要配置数据源属性,这样指定该事务管理器 是对哪个数据源进行事务控制
-->
<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--配置启动基于注解的声明式事务管理功能-->
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>事务的传播机制



事务隔离级别

Spring
本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。
评论交流
欢迎留下你的想法