Skip to main content

Spring Core

Specifying a Short-lived Bean in a Long-lived Bean

  1. 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>
  1. 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

  1. 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();
}
  1. 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;
}
  1. 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

  1. 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 + '\'' +
'}';
}
}
  1. 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;
}
}
}
  1. 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;
}
}
  1. 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");
}
}