Spring Core
Specifying a Short-lived Bean in a Long-lived Bean
- XML Configuration Method
proxy-target-class
default=true
uses CGlib for proxying; default=false
uses JDK dynamic proxy;
XML Configuration
<!-- DefaultUserPreferences implements the UserPreferences interface -->
<bean id="userPreferences" class="com.stuff.DefaultUserPreferences" scope="session">
<aop:scoped-proxy proxy-target-class="false"/>
</bean>
<bean id="userManager" class="com.stuff.UserManager">
<property name="userPreferences" ref="userPreferences"/>
</bean>
- Annotation Method
// CGLIB method
@Scope(value="request", proxyMode = ScopedProxyMode.TARGET_CLASS)
// JDK dynamic proxy method
@Scope(value="request", proxyMode = ScopedProxyMode.INTERFACES)
Prototype Bean in Singleton Bean
Method 1: lookup
, through lookup it will automatically search for beans of the same type for injection; note that methods using lookup must be abstract methods;
@Component("AAA")
@Scope(value = "prototype")
public class AAA {
@Bean
public AAA createAAA(A a,B b) {
return this;
}
}
@Component
public abstract class ControllerManager {
private AAA aaa;
@Lookup
public abstract AAA createAAA() ;
public void test() {
this.aaa = createAAA();
}
}
Method 2: Implement ApplicationContextAware
to get the beanFactory
object, each method call will get a new singleton bean
@Autowired
Field Underlying Implementation
@Autowired
first injects by type, if there are multiple types, then injects by name; in the source code, setting values is implemented through reflection org.springframework.beans.DirectFieldAccessor.FieldPropertyHandler#setValue
@Override
public void setValue(@Nullable Object value) throws Exception {
try {
ReflectionUtils.makeAccessible(this.field);
this.field.set(getWrappedInstance(), value);
} catch (IllegalAccessException ex) {
throw new InvalidPropertyException(getWrappedClass(), this.field.getName(),
"Field is not accessible", ex);
}
}
@Autowired
or @Resource
Specifying Implementation Classes
- Use
@Primary
on a specific sub-implementation class to specify the Bean to be injected as the current bean, choose one of the following injection methods
@Primary
@Component
public class ProtoBeanImpl implements ProtoBean{
}
@Primary
@Bean
public ProtoBeanImpl protoBean(){
return new ProtoBeanImpl();
}
- Use
@Qualifier("xxx")
annotation to specify the type of bean to be injected
@Qualifier("protoBeanImpl2")
@Autowired
private ProtoBean bean;
@Autowired
public SingleBean(@Qualifier("protoBeanImpl2") ProtoBean proto) {
this.protoBeanImpl2 = (ProtoBeanImpl2) proto;
}
- Use
@Resource
@Resource(name="protoBeanImpl2")
private ProtoBean protoBean;
// Equivalent to the following, if name is not specified, defaults to variable name
@Resource
private ProtoBean protoBeanImpl2;
Constructor Injection
-
Using constructor inject, do not need other annotation if all the properties is base type, using
@ConstructorProperties({"xxx","xxx",...})
to inject the value -
If just one constructor here, need not
@Autowired
-
Only one multi-argument constructor can be set
@Autowired(required = true)
-
If one more constructor are annotated with
@Autowired(required = false)
The constructor with the greatest number of dependencies that can be satisfied by matching beans in the Spring container will be chosen -
The
@Autowired
,@Inject
,@Value
, and@Resource
annotations are handled by Spring BeanPostProcessor implementations
Using AspectJ Annotations for AOP Configuration vs XML
When using XML configuration, AOP configuration is scattered, part in XML and part in backend class files. This doesn't follow the DRY principle.
Using @AspectJ
, the entire AOP configuration is placed in one configuration class. @AspectJ
supports additional instance models with richer combinations, making each aspect a model unit.
At the same time, @AspectJ
can be parsed by both Spring AOP and AspectJ, allowing you to use AspectJ syntax to implement more complex aspect logic
Closures and Callbacks
Closure: Closures and anonymous functions are often used as synonyms. But strictly speaking, an anonymous function is literally a function that has not been given a name, while a closure is actually an instance of a function, that is, a structure that exists in memory. From an implementation perspective, if an anonymous function doesn't capture free variables, it can actually be implemented as a function pointer, or directly inlined to the call site. If it captures free variables, then it will be a closure; while a closure means including both function pointer and environment as two key elements. Reference Closure Sample
public interface Adder {
int add(int x);
}
public static Adder makeAdder(final int n) {
return new Adder() {
public int add(int x) {
return x + n;
}
};
}
Callback: In computer programming, a callback function, or simply callback (Callback i.e., call then back - called by the main function and returns to the main function after computation), refers to passing a function through parameters to its code, a reference to some executable code Reference Callback Sample
class RemoteClass {
private OnChangeListener mOnChangeListener;
void makeSomeChanges() {
/*
.. do something here and call callback
*/
mOnChangeListener.onChanged(this, 1);
}
public void setOnChangeListener(OnChangeListener listener) {
mOnChangeListener = listener;
}
public interface OnChangeListener {
void onChanged(RemoteClass remoteClass, int test);
}
}
class Test {
public static void main(String[] args) {
RemoteClass obj = new RemoteClass();
obj.setOnChangeListener(demoChanged);
obj.makeSomeChanges();
}
// this case remind me of spring framework lots of anonymous ObjectFactory call back
private static RemoteClass.OnChangeListener demoChanged = new RemoteClass.OnChangeListener() {
@Override
public void onChanged(RemoteClass remoteClass, int incoming) {
switch (incoming) {
case 1:
System.out.println("I will take appropriate action!");
break;
default:
break;
}
}
};
}
// callback in springframework 4.3.x
// @see org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean
// Create bean instance.
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);
throw ex;
}
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
@Configurable
@Configuration
@Configuration
is a configuration class
@Configurable
is an Aspect that a bean depends on, and is not managed by Spring, but can be auto-injected
Spring Boot Custom Event Publishing
This example uses SpEL
(Spring Expression Language) expressions
- Define your own event
MyEvent
/**
* @Author: WhaleFall541
* @Date: 2021/7/12 21:38
*/
public class MyEvent extends ApplicationEvent {
private final String address;
private final String content;
public MyEvent(Object source, String address, String content) {
super(source);
this.address = address;
this.content = content;
}
// getter and setter omit
@Override
public String toString() {
return "MyEvent{" +
"address='" + address + '\'' +
", content='" + content + '\'' +
'}';
}
}
- Custom event publishing class
MyEventPublish
/**
* @Author: WhaleFall541
* @Date: 2021/7/12 21:41
*/
@Component
public class MyEventPublish implements ApplicationEventPublisherAware {
private ApplicationEventPublisher publisher;
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.publisher = applicationEventPublisher;
}
public void publish(String address, String content) {
if ("aaa".equals(address)) {
publisher.publishEvent(new MyEvent(this, address, content));
return;
}
}
}
- Write event listener class
/**
* @Author: WhaleFall541
* @Date: 2021/7/12 21:38
*/
@Component
public class MyListener implements ApplicationEventPublisherAware {
private Log log = LogFactory.getLog(MyListener.class);
// SpEL #a0 represents the first parameter
// @EventListener(condition = "#a0.content == 'foo'")
// SpEL #event represents parameter with the same name
// @EventListener(condition = "#event.content == 'foo'")
// If you want to represent an object, use @XXX testMethod is the bean name in the container
@EventListener(condition = "@testMethod.test().equals(#a0.content)")
// @EventListener({ContextStartedEvent.class, ContextRefreshedEvent.class})
public void process(MyEvent event) {
System.err.println("event test is ok " + event);
}
}
TestMethod
class
@Component
public class TestMethod {
public List<String> test() {
List<String> al = new ArrayList();
al.add("foo");
al.add("fool");
return al;
}
}
- Test class
Note that you need to import dependencies, here is the gradle
dependency configuration testImplementation 'org.springframework.boot:spring-boot-starter-test'
@SpringBootTest
class Charter1Tests implements ApplicationContextAware {
@Test
void testApplicationEvent() {
MyEventPublish bean = context.getBean(MyEventPublish.class);
bean.publish("aaa", "foo");
}
}