Bean的管理
获取
注入
- BeanFactory 是 Spring的一个Bean 的工厂,使用简单工厂模式,是 ApplicationContext 的父类,IOC 容器的容器顶级接口,负责生产、实例化、配置Bean对象以及建立这些Bean对象间的依赖。BeanFactory 实例化后并不会自动去实例化 Bean(是说他生产的bean),只有当 Bean 被使用时才实例化并装配依赖关系,属于延迟加载,适合多例模式。
- 可以通过BeanFactory.getBean(“”)获取Bean
- 范围可以是一个xml文件
- FactoryBean 则是一个Bean,使用了工厂方法模式,作用是生产其他 Bean 实例,可以通过实现该接口,提供一个工厂方法来自定义实例化 Bean 的逻辑。FactoryBean 接口由 BeanFactory 中配置的对象实现,这些对象本身就是用于创建对象的工厂,如果一个 Bean 实现了这个接口,那么它就是创建对象的一个工厂 Bean,而不是 Bean 实例本身。
- FactoryBean是默认是单例的,其default方法 isSingleton()默认直接返回true
- 为什么Spring想到注入?
- 注入解决了什么问题?Spring是在什么情况下想到工厂方式注入bean的?可以总结下当作设计模式的案例。
动态代理
jdk动态代理
一切皆对象真是好呐,类的信息有Class对象,方法有Method对象来直接调用
jdk动态代理需要基于接口实现(如果使用继承某个类来实现的话,会受到java只允许单继承机制的限制)
jdk动态代理会为目标对象生成一个继承了Proxy类的代理类对象。
下文一个AI生成的案例:利用反射原理,通过一个目标对象,获取其代理对象
欸!像这样↑开头先说清楚入参出参就好办了
getProxyInstance方法源码
private Object target;
public Object getProxyInstance() {
return Proxy.newProxyInstance(
target.getClass().getClassLoader(), // 用于加载被代理类
target.getClass().getInterfaces(), //Class<?>[] interfaces数组
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("前置");
Object result = method.invoke(target, args);
System.out.println("后置");
return result;
}
}
);
通过目标对象获取其类的ClassLoader和接口,据此生成(代理类的字节码cl?)
然后获取其带有InvocationHandler的构造器,(可代理类的父类Proxy里带有。可能基于接口代理是因为生成的代理类必须在子级,但是又必须继承这个Proxy?那他咋恁贱类,非得继承?可能有单例的实例化吧。),并据此创建代理对象
在生成的代理类中,会生成和原对象同名的方法(根据接口?),而方法的实现则是调用我们传入的InvocationHandler 的invoke方法中,我们自己实现的、包装了原方法的逻辑。
//反编译生成的代理类字节码文件,可以看到:代理类继承了Proxy类,实现了Singer接口
public final class $Proxy0 extends Proxy implements 目标对象类{
public final boolean 原方法名(Object paramObject)
throws {
try
{ // m1,m2,m3:反编译得来private static Method 属性,既被代理的原方法。
this.h.invoke(this, m3, new Object[] { paramString });
}
// ...
}
}
再通过Spring的IOC管理,实际使用时自动注入代理对象给接口,而调用代理对象的方法,自然是代理生成的包装后的方法。
/**
* 这也是多态特性的一个体现:其实这里给接口注入的是同样实现了接口的代理对象,而不是目标对象;
* 因为接口可以引用任意实现类,这一操作自然可以实现。当我们后面调用userService方法的时候,实际上是在调用代理类中同名的方法。
*/
@Resource("userService")
IUserService userService;
拓展:Proxy#newProxyInstance 源码1
可作为工厂模式案例
其实并没有挖出什么东西,,挖到defineClass0就停了,了解到的仍然还是"ClassLoader+接口获取代理类字节码。可能这其中,因为有接口之类的,所以生成了接口中的方法吧。"
通过ClassLoader
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException{
// ...
// 将接口类对象数组clone一份。
final Class<?>[] intfs = interfaces.clone();
//执行权限检查
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
/*
* Look up or generate the designated proxy class.
*/
// 查找或者是生成一个特定的代理类对象? todo 这里是字节码吧
Class<?> cl = getProxyClass0(loader, intfs);
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
// 是static final 修饰的,源码: private static final Class<?>[] constructorParams ={ InvocationHandler.class };
// 从代理类对象中查找参数为InvocationHandler的构造器
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
// 检测构造器是否是Public修饰,如果不是则强行转换为可以访问的。
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
// 通过反射,将InvocationHandler 作为参数,实例化代理类,返回代理类实例。
return cons.newInstance(new Object[]{h});
}
// ...
}
getProxyClass0代码嵌套了两层缓存用的内部子类的map,终于到了ProxyClassFactory
/**
* 一个利用给定的类加载器和接口类数组进行生成、定义并返回代理类对象的工厂方法
* A factory function that generates, defines and returns the proxy class given
* the ClassLoader and array of interfaces.
*/
private static final class ProxyClassFactory
implements BiFunction<ClassLoader, Class<?>[], Class<?>>
{
// ...
@Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
// ...
/*
* Generate the specified proxy class.
*/
//生成代理类class文件
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);
try {
// 返回代理类对象 参数:ClassLoader, 代理类类名,代理类.class字节码信息
return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
// ...
}
}
}
}
-
不知道为什么私有方法不能代理。是因为目标类不能继承所有就不搞了?是因为接口不会有有意义的私有声明?
-
spring 5.x仍然默认使用jdk动态代理
-
SpringBoot 2.x开始,AOP为了解决使用JDK动态代理可能导致的类型转换异常,而使用CGLIB。
-
在SpringBoot 2.x中,AOP如果需要替换使用JDK动态代理可以通过配置项spring.aop.proxy-target-class=false来进行修改,proxyTargetClass配置已无效。
SPI
前置知识:
- ClassLoader
数据
DAO支持
@Repository定位同DAO。文档中经常用or同时称呼他们
@Repository
public class JdbcMovieFinder implements MovieFinder {
private JdbcTemplate jdbcTemplate;
@Autowired
public void init(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
// ...
}
- 为什么 @Autowired在方法上而不是直接注入?
- 话说此处说明DAO依赖其具体实现(需要用具体实现去set)。但通过依赖注入,不用每次手动set了?
- 那如果单例模式每次获得这个对象不是一个效果吗
@Repository
public class HibernateMovieFinder implements MovieFinder {
private SessionFactory sessionFactory;
@Autowired
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
// ...
}
- 下面这个是类似行数据入口的模式吗?顶一个DAO,然后执行方法,其内部实现,,emm,那个时候还只有jdbc这种级别吧,没牵扯到ORM。所以数据映射器后面单独说了
String lastName = this.jdbcTemplate.queryForObject(
"select last_name from t_actor where id = ?",
String.class, 1212L);
JDBC编程in Spring
wow,新的数据mapper方式出现了!这画中倒没说是ORM mapping
public class ActorMappingQuery extends MappingSqlQuery<Actor> {
public ActorMappingQuery(DataSource ds) {
super(ds, "select id, first_name, last_name from t_actor where id = ?");
declareParameter(new SqlParameter("id", Types.INTEGER));
compile();
}
@Override
protected Actor mapRow(ResultSet rs, int rowNumber) throws SQLException {
Actor actor = new Actor();
actor.setId(rs.getLong("id"));
actor.setFirstName(rs.getString("first_name"));
actor.setLastName(rs.getString("last_name"));
return actor;
}
}
事务
声明式事务管理:xml 或注解方式
编程式事务管理:TransactionTemplate
- 看来用XXXTemplate 几乎都是编程式管理了?
@Transactional 事务原理
在方法开始前开启事务
检查异常(unchecked exception)不回滚
如果想针对检查异常进行事务回滚,可以在@Transactional
注解里使用 rollbackFor
属性明确指定异常。
@Transactional(rollbackFor = Exception.class)
推荐做法:在业务层统一抛出异常,然后在控制层统一处理。
这样业务层一直在抛异常,就保证了事务会回滚;同时异常可以在控制层统一处理了
Transactional
注解的常用属性表:
属性 | 说明 |
---|---|
propagation | 事务的传播行为,默认值为 REQUIRED。 |
isolation | 事务的隔离度,默认值采用 DEFAULT |
timeout | 事务的超时时间,默认值为-1,不超时。如果设置了超时时间(单位秒),那么如果超过该时间限制了但事务还没有完成,则自动回滚事务。 |
read-only | 指定事务是否为只读事务,默认值为 false;为了忽略那些不需要事务的方法,比如读取数据,可以设置 read-only 为 true。 |
rollbackFor | 用于指定能够触发事务回滚的异常类型,如果有多个异常类型需要指定,各类型之间可以通过逗号分隔。{xxx1.class, xxx2.class,……} |
noRollbackFor | 抛出 no-rollback-for 指定的异常类型,不回滚事务。{xxx1.class, xxx2.class,……} |
Controller层使用事务注解会生效吗?
1.对于Spring + SpringMvc架构中,@Transactional被Spring容器管理,而Controller则被SpringMvc容器进行管理, 所以在Spring + SpringMvc架构中,我们直接在Controller层使用@Transactional,此时事务是不生效的。
2.对于SpringBoot架构,@Transactional和Controller统一由SpringBoot容器进行管理,因此我们在Controller层使用@Transactional开启事务是可以生效的。
存疑
javax与Spring的@Transactional
jakarta.transaction.Transactional 也可以即插即用得替换Spring的事务注解。
stereotype
Spring的组件管理注解同JSR-330标准注解有些不同。
Component
@Serivce
@Component的别名
@Configuration
@Bean
@Bean注解可在@Configuration 或 @Component 注解的类中使用。
该注解标记在方法上,默认会注册一个Bean定义,其名称为方法名,ApplicationContext 为方法返回值的类型
拓展
https://www.cnblogs.com/zhangchengzi/p/9713807.html 详细但内容很多的源码,颇受java“一层套一层”的荼毒。本文提取了其主要部分 ↩︎