#Spring 4.3
These are just my personal notes I took during the 4 day SpringSource Core Spring training.
Reflective notes.
Some important concepts of Spring Framework
- Focus on POJO's that have a Single Purpose
- Program to interfaces
- Testable code
- Low Coupling
- High Cohesion
Take a look at the mock exams or use the interactive quiz online to test your knowledge.
##Spring Configuration
- Java Configuration classes
- XML Configuration
- Annotations
###Java Configuration Class
Functions should be declared public
.
Each bean is given a unique id / name.
Bean ids should not contain implementation detail.
@Configuration
open class RewardsConfig {
@Autowired lateinit var dataSource: DataSource
@Bean
open fun rewardNetworkImpl(accountRepository: JdbcAccountRepository,
restaurantRepository: JdbcRestaurantRepository,
rewardRepository: JdbcRewardRepository) =
RewardNetworkImpl(
accountRepository,
restaurantRepository,
rewardRepository
)
@Bean
open fun accountRepository() = JdbcAccountRepository().apply { setDataSource(dataSource) }
@Bean
open fun restaurantRepository() = JdbcRestaurantRepository().apply { setDataSource(dataSource) }
@Bean
open fun rewardRepository() = JdbcRewardRepository().apply { setDataSource(dataSource) }
}
###Disambiguation
Beware of duplicate bean definitions.
It is not illegal to define the same bean more than once. You will just get the last bean Spring
sees defined.
To prevent this behavior look at @Qualifier
@Component("transferService")
public class TransferServiceImpl implements TransferService {
@Autowired
public TransferServiceImpl(@Qualifier("jdbcAccountRepository")
AccountRepository accountRepository) { ...}
}
@Component("jdbcAccountRepository")
public class JdbcAccountRepository implements AccountRepository {..}
@Component("jpaAccountRepository")
public class JpaAccountRepository implements AccountRepository {..}
###Bean Resolution
The way Spring finds the correct bean is in following order
- Look for unique bean of required type
- Use
@Qualifier
if supplied - Try to find a mathing bean by name
- Is is very much a fallback so you should avoid using this convention and make it more explicit
####Explicit vs Implicit Bean Definition (Annotation Driven)
Above is an example of an explicit bean definition file. Namely you provide all the beans in a configuration.
Implicit autoMagically
tries and search for your Components
Implicit Configuration
@Component
public class TransferServiceImpl implements TransferService {
@Autowired
public TransferServiceImpl(AccountRepository repo) {
this.accountRepository = repo;
}
}
@Configuration
@ComponentScan("com.bank")
public class AnnotationConfig {
// No bean definition needed any more
}
@ComponentScan
searches for your beans in a particular package.
####Java Configuration vs Annotations
#####Java Configuration
Pro's | Cons |
---|---|
Is centralized in one (or a few places) | More Verbose than annotations |
Write any Java code you need | |
Strong type checking enforced by compiler (and IDE) | |
Can be used for all classes (not just your own |
#####Annotations
Pro's | Cons |
---|---|
Single place to edit (just the class) | Configuration spread across your code base |
Allows for very rapid development | Only works for your own written code |
Merges configuration and code (bad sep. of concerns) |
Best pratice is to use annotations as much as possible.
In real life you will see these two being mixed.
Beans that are maintained by the company will be annotated and legacy code or code from dependencies
will live in a Java configuration file.
One example where a Java configuration would be preferred is for a Datasource
that you need to
configure with different parameters
####Descriptors
You can define descriptors on the bean definitions.
With the @Descriptor
annotation
This is comparable to a Javadoc
for a bean.
####Imports
Organize your @Configuration
classes however you like.
Best practive: seperate out application
beans from infrastructure
beans.
You can import
other configuration files with @import
on top of the class declaration.
@Configuration
@Import(TestInfrastructureConfig::class)
open class RewardsConfig { ... }
####Start the context
Start the ApplicationContext
Spring Boot:
val context: ApplicationContext = SpringApplication.run(RewardsConfig::class.java)
Using XML:
SpringApplication.run(MainConfig.class);
@Configuration
@ImportResource( {
"classpath:com/acme/application-config.xml",
"file:C:/Users/alex/application-config.xml" } )
@Import(DatabaseConfig.class)
public class MainConfig {}
Valid prefixes are:
- classpath (default)
- file
- http
Multiple files are possible Able to combine with
@Configuration
annotations
Some other (older) ways to load the Context
// Load Java Configuration class
new AnnotationConfigApplicationContext(MainConfig.class);
// Load from $CLASSPATH/com/acme/application-config.xml
new ClassPathXmlApplicationContext(“com/acme/application-config.xml”);
// Load from absolute path: C:/Users/alex/application-config.xml
new FileSystemXmlApplicationContext(“C:/Users/alex/application-config.xml”);
// Load from path relative to the JVM working directory
new FileSystemXmlApplicationContext(“./application-config.xml”);
####Get beans from context
Get beans directly from the context There are three ways:
- Classic way: cast is needed
val transferService = context.getBean("transferService") as TransferService
- Use typed method to avoid cast
val transferService = context.getBean("transferService", TransferService::class.java)
- No need for bean id if type is unique
val rewardNetwork = rewardNetwork = context.getBean(RewardNetwork::class.java)
####Bean Scope
A scope defines how your beans are defined.
Examples of scopes are:
- singleton
- prototype
- session (new instance created per user session)
- request (new instance per request)
Custom scopes are allowed, but are rarely used.
#####Singleton
Default a bean is scoped as a singleton
.
@Bean
fun accountService() : AccountService = AccountService()
//Equivalent
@Bean
@Scope("singleton")
fun accountService() : AccountService = AccountService()
#####Prototype A new instance is created every time bean is referenced
@Bean
@Scope("prototype")
fun accountService() : AccountService = AccountService()
Old way of configuring your beans.
Spring pushes java configuration instead of XML.
Example of a XML Configuration
<beans profile=“prod”>
<bean id=“transferService” class=“com.acme.TransferServiceImpl”>
<property name=“repository” ref=“accountRepository” />
</bean>
<bean id=“accountRepository” class=“com.acme.JdbcAccountRepository”>
</bean>
</beans>
You should now only be able to understand how you would apply the same principles as with Java Configuration for the XML configuration.
####FactoryBean Interface
Is a fallback for complex configuration in XML.
implementing FactoryBean<T>
interface allows you to have the factory pattern in place for XML
configurations.
Even Java Configuration may use factory beans
They are widely used in older frameworks
Some well known examples are:
- EmbeddedDatabaseFactoryBean
- JndiObjectFactoryBean
- Creating remote proxies
- Creating caching proxies
- For configuring data access technologies
####Namespaces
Namespaces allow hiding of actual bean definitions you may want to add.
They can greatly reduce the XML configuration file.
<context:property-placeholder location="db-config.properties" /> <!-- Hides 1 bean definition -->
<aop:aspectj-autoproxy /> <!-- Hides 5 bean definitions -->
<tx:annotation-driven /> <!-- Hides more than 15 bean definitions -->
Common practice do not use a versioned
*.xsd
file. Always go for the un-versioned. This will allow you to always be on the latest version. And makes sure you have an easier migration.
Some examples are:
- aop (aspect oriented programming)
- tx (transactions)
- util
- jms
- context
##Property Values
Externalize your properties.
It allows for a more readable and flexible way of configuring your Spring application.
###Environment
Get environmental variables from the runtime environment
Environment Variables and Java System Properties are always populated automatically
@Bean
fun dataSource(): DataSource {
val ds = BasicDataSource()
ds.setDriverClassName(env.getProperty(DB_DRIVER))
ds.setUrl(env.getProperty(DB_URL))
ds.setUser(env.getProperty(DB_USER))
ds.setPassword(env.getProperty(DB_PWD))
return ds
}
Environment obtains values from property sources
@PropertySource
contributes additional properties
@Configuration
@PropertySource("classpath:/com/organization/config/app.properties")
@PropertySource("file:config/local.properties")
class ApplicationConfig {
@Bean
fun dataSource(
@Value("${db.driver}") driver : String,
@Value("${db.url}") url : String,
@Value("${db.user}") user : String,
@Value("${db.password}") password : String) {
}
}
How are these
${ ... }
evaluated?
They need a dedicated bean: PropertySourcesPlaceholderConfigurer
NOTE A static bean otherwise this still won't work
@Bean
public static PropertySourcesPlaceholderConfigurer pspc() {
return new PropertySourcesPlaceholderConfigurer();
}
###Profiles Beans can be grouped according to for example their environment they live in.
Beans without a profile will always be available / loaded
They way you define them is with the @Profile
annotation.
You can either place them on a class or a method header.
This way you can split up your configuration file for each environment.
@Configuration
@Profile("dev")
class DevConfig
or
@Bean(name="dataSource")
@Profile("dev")
@PropertySource("dev.properties")
fun dataSrouceForDev() : DataSource
@Profile
can control which@PropertySources
are included in the Environment
How do you active these profiles at run-time?
-Dspring.profiles.active=dev,jpa #dev profile and jpa profile are active now.
Or pragmatically
System.setProperty("spring.profiles.active", "dev, jpa");
SpringApplication.run(AppConfig.class);
Under tests (and only then) you can use the @ActiveProfiles
@ActiveProfiles("dev, jpa")
class Test
##SPEL
Short for Spring Expression Language
Allows you to inject some logic in a String
value
@Configuration
class TaxConfig
{
@Value("#{ systemProperties['user.region'] }") String region;
@Bean public TaxCalculator taxCalculator1() {
return new TaxCalculator( region );
}
@Bean public TaxCalculator taxCalculator2
(@Value("#{ systemProperties['user.region'] }") String region, ...) {
return new TaxCalculator( region );
}
}
Equivalent way of getting to properties so:
@Value("${daily.limit}")
int maxTransfersPerDay;
is the same as in SPEL
@Value("#{environment['daily.limit']}")
int maxTransfersPerDay;
However you don't need to define the PropertySourcesPlaceholderConfigurer
bean.
Properties returned by this always return a Sring
.
Sometimes you therefore have to cast it if you want to do some calculations on it.
@Value("#{new Integer(environment['daily.limit']) * 2}")
@Value("#{new java.net.URL(environment['home.page']).host}")
Attributes SPEL can retrieve include:
- Spring Beans
- Implicit references
- Spring Environment
- System Properties
- System Environments
- Others depending on context
##Annotations in Spring
###@Autowired
Autowired can be used to inject
- Constructors
- Methods with arguments
- Setters
- Fields
The
fields
can even beprivate
, but it makes it harder to test
Since Spring follows the standards for DI you can also use
@Inject
Default @Autowired
dependencies are required.
Override this default behavior when you want
public void setAccountRepository(AccountRepository a) {
this.accountRepository = a;
}
####Setter- vs Constructor Injection
Constructors | Setters |
---|---|
Mandatory Dependencies | Optional / Changeable dependencies |
Immutable Dependencies | Circular dependencies |
Concise (pass several params at once) | Inherited Automatically |
If constructor needs too many parameters |
As a general rule thought just follow your teams convention.
###@Value
Value can be used to inject
- Constructors
- Methods with arguments
- Fields
####Default Value
If @Value
is resolved and returns null you can provide a default value.
@Autowired
public TransferServiceImpl(@Value("${daily.limit : 100000}") int max) {
this.maxTransfersPerDay = max;
}
For SPEL you use the Elvis Operator
@Autowired
public setLimit(@Value("#{environment['daily.limit'] ?: 100000}") int max) {
this.maxTransfersPerDay = max;
}
###@ComponentScan
Components are scanned at startup
Note that dependencies are also scanned. Therefor you should always define a
basePackage
as this may drastically improve startup time.
@ComponentScan ({"com.bank.app.repository", "com.bank.app.service", "com.bank.app.controller"})
###@PostConstruct & @PreDestroy
####@PostConstruct
Method called at startup after dependency has done all its injections.
So after constructors and setter injections
Will only work when
@ComponentScan
is defined in the configuration file.
Can take any visibility, but must take no parameters and only return void
####@PreDestroy
Method called at shutdown prior to destroying the bean instance.
Called when a ConfigurableApplicationContext
is closed.
If application (JVM) exits normally
Note that this is not the destruction of the object itself as we know it in Java.
Will only work when
@ComponentScan
is defined in the configuration file.
public class JdbcAccountRepository {
@PreDestroy
public void clearCache() { ... }
}
You can expliticly let this callback be called
ConfigurableApplicationContext context = SpringApplication.run(...);
context.close();
Can take any visibility, but must take no parameters and only return void
####Java Config way
@Bean (initMethod="populateCache", destroyMethod="clearCache")
public AccountRepository accountRepository() {
// ...
}
###Stereotype Annotations
Stereotype annotations are syntactic sugar for @Component
They inherit from @Component
so when you apply the @ComponentScan ( "..." )
Spring finds them.
Examples of predefined stereotype annotations are:
- Service
- Repository
- Controller
- Configuration
Other Spring projects may add in their own stereotype annotations.
###@Resource
Identifies dependencies by name not by type.
The difference with @Autowired
is that it will first check by name before type
Setter injection
@Resource(name="jdbcAccountRepository”)
public void setAccountRepository(AccountRepository repo) {
this.accountRepository = repo;
}
Field injection
@Resource(name="jdbcAccountRepository”)
private AccountRepository accountRepository;
##Bean Lifecycle
Spring fits in to manage your application lifecycle. It may play an important role in all phases.
The lifecycle only applies to any class of application
- Standalone Java or Spring Boot application
- Integration/System Test
- Java EE
###Initialization
- Prepares for use
- Application services
- are created
- configured
- may allocate system resources
- Application is not usable until this phase has completed
####Load Bean Definitions
- The
@Configuration
classes are processed and@Components
are scanned and or XML files are parsed - Bean definitions get added to the BeanFactory
- Special BeanFactoryPostProcessor beans invoked
After that each bean is instantiated. Created in the correct order.
Next each bean goes through a post-processing phase BeanPostProcessors
Now the bean is fully initialized and ready to be use
####BeanFactoryPostProcessor
Applies transformations to bean definitions (so before the object are created)
Some use cases involve reading properties and registering custom scopes.
For example the PropertySourcesPlaceholderConfigurer
You can write your own, but it is very uncommon. You do this by implementing the
BeanFactoryPostProcessor
interface
####Initializer Extension Point
A special case of BeanPostProcessor.
It causes the @PostConstruct
to be called.
The way this works is by using a BPP for example the CommonAnnotationBeanPostProcessor
enables
this functionality
####BeanPostProcessors Extension Point
Important extension point in Spring. This allows it to modify bean instance in any way.
In order to use this functionality the user must implement the BeanPostProcessor
interface.
They will automatically be picked up by the Spring Framework.
####Configuration Lifecycle
###Use
When you invoke a bean obtained from the context.
Spring can add functionality to your requested object this is why the proxy pattern
has been
implemented on your Spring beans. This happened during the initialization phase of the lifecycle.
Spring uses either the build in proxy
classes available in the JDK
or use a library called
CGLIB
to do more advanced proxy-ing.
- Used by clients
- Application services
- Process client requests
- Carry out application behaviors
- 99.9% of time is spent in this phase
JDK Proxy | CGLib Proxy |
---|---|
Also called dynamic proxies | NOT built into JDK |
API is built into the JDK | Included in Spring jars |
Requirements: Java Interface(s) | Used when interfaces not available |
All interfaces proxied | Cannot be applied to final classes or methods |
###Destruction
When you close a context the destruction phase completes.
It destroys beans instances if instructed and calls their clean-up methods. Beans must have a destroy method defined (a no-arg method returning void)
After the context is destroyed it can not be used again.
- Shuts down
- Application services
- release any system resources
- are eligible for GC
##Testing Spring
###Unit testing
Basic JUnit4
tests
###Integration testing
Tests the interaction of multiple units working together.
Central support class is called SpringJUnit4ClassRunner
since Spring 4.3 they added a shorten name
class called: SpringRunner
.
This class allows you to share an ApplicationContext
####Databases
It is very common to test databases in integration tests.
You can use an in memory database for this.
Not always a good fit since you want to keep your tests as close as possible to the real environment.
Common requirement is to populate the DB before every test run.
We can use the @Sql
annotation for this.
@Sql(scripts = "/test-user-data.sql",
executionPhase = ExecutionPhase.AFTER_TEST_METHOD,
config = @SqlConfig(
errorMode = ErrorMode.FAIL_ON_ERROR,
commentPrefix = "//", separator = "@@"
)
)
@Test
fun testFunctionName() { }
##AOP
Aspect Oriented Programming.
Generic functionality that is needed in many places in your application.
When you want to implement a feature and you describe this feature as following:
Perform a role-based security check before EVERY application method.
It is a clear sign this requirement is a cross-cutting concern
Examples:
- Logging and Tracing
- Transactional Management
- Security
- Caching
- Error Handling
- ...
###Limitations
- Can only advise non-private methods.
- Can only apply aspects to Spring Beans
- Limitations of weaving with proxies
- consider the following: When using proxies suppose a method
a()
calls methodb()
on the same class/interface the advice will never be executed for methodb()
- consider the following: When using proxies suppose a method
###How
Spring creates a proxy around your object.
This new proxy object intercepts all the request to this object. This way you can decide to act upon it.
###AOP Concepts
####Join Point
A point in the execution of a program such as a method call or when an exception is thrown.
Spring AOP uses AspectJ's pointcut expression language for selecting where to apply advice.
Spring AOP supports a practical subset
####Pointcut
An expression that selects one or more Join Points
####Advice
Code to be executed at each selected Join Point
####Aspect
A module that encapsulates pointcuts and advice
####Weaving
Technique by which aspects are combined with main code
###Implement the Aspect
Best practice is to keep all aspects inside of a package.
This way you can have one configuration class that keeps them all together.
You should annotate the configuration class with yet another annotation @EnabledAspectJAutoProxy
in order
for Spring to pick up all your aspects.
@Configuration
@EnableAspectJAutoProxy
@ComponentScan(basePackages=“com.example”)
class AspectConfig
###Writing Expressions
Examples
execution(void send*(String))
Any method starting with send that takes a single String parameter and has a void return type.
execution(* send(int, ..))
Any method named send whose first parameter is an int (the .. signifies 0 or more parameters may follow)
You can also restrict by class
execution(void example.MessageServiceImpl.*(..))
Any void method in hte MessageServiceImpl class (including sub-classes)
Ignored when another implementation is used.
Or by interface
execution(void example.MessageService.send(*))
Any void method send taking one argument in any object implementing MessageService
More flexible choice - works if implementation changes.
Or even by annotation
execution(@javax.annotation.security.RolesAllowed void send*(..))
Any void method whose name starts whit send that is annotated with the @RolesAllowed
annotation.
Ideal for your own classes.
This is also how a lot of the build in Spring AOP aspects work like @Transactional
Restrict by package
execution(* rewards..restaurant.*.*(..))
There may be several directories between rewards and restaurant
execution(* *..restaurant.*.*(..))
Any sub-package called restaurant
####Before advice
Using the @Before
annotation
An example of a logging aspect that monitors all setter methods.
@Aspect
@Component
public class PropertyChangeTracker {
private Logger logger = Logger.getLogger(getClass());
@Before("execution(void set*(*))")
public void trackChange(Joinpoint point) {
String name = point.getSignature().getName();
Object newValue = point.getArgs()[0];
logger.info(name + " about to change to " + newValue + " on " + point.getTarget());
}
}
With the help of the
JoinPoint
object we get more information.
####AfterReturning Advice
Run after the target has done it's thing
@AfterReturning(value="execution(* service..*.*(..))",
returning="reward")
public void audit(JoinPoint jp, Reward reward) {
auditService.logEvent(jp.getSignature() +
"returns the following reward object :" + reward.toString() );
}
You get the value Reward that was returned by the target.
####AfterThrowing
Only invokes advice if the right exception type is thrown.
The @AfterThrowing
advice will NOT stop the exception from propagating.
However you can map it to a different type of exception.
@AfterThrowing(value="execution(* *..Repository.*(..))", throwing="e")
public void report(JoinPoint jp, DataAccessException e) {
mailService.emailFailure("Exception in repository", jp, e);
}
####After
Called regardless of whether an exception has been thrown by the target or not.
####Around
Inherits from JoinPoint
and adds the proceed()
method.
Combination of @Before
and @After
Use the point.proceed()
on the ProceedingJoinPoint
object to control the flow of the target.
#This is just here to allow for some easy copying
Current page number: 99