~~~
~~~
public class Test {
public static void main(String[] args) {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(HelloConfig.class);
// hello1, hello2 는 동일한 인스턴스이다.
Hello hello1 = ac.getBean(Hello.class).getHello();
Hello hello2 = ac.getBean(HelloConfig.class).hello();
}
}
@Configuration
@EnableHello // Import를 좀 더 명시적으로 사용하는 방법
//@EnableHello("Spring") // ImportAware 예제 케이스 (옵션 정보를 넘기는 방법)
//@EnableHello(type=1) // ImportSelector 예제 케이스 (설정정보의 분기를 처리하는 방법)
//@EnableHello(type=1) // ImportBeanDefinitionRegistrar 예제 케이스 (커스터마이징 방법이 가장 높은 방법 - @Bean 사용대신 직접 Bean 설정)
public class AppConfig {
}
//1) @Import(HelloConfig.class) // 특정 Configuration을 지정해서 Import한다.
//2) @Import(HelloSelector.class) // Import할 Configuration을 판단하는 Selector를 Import한다.
//2) @Retention(RetentionPolicy.RUNTIME) // 해당 어노테이션이 언제까지 살아있는가? Default Retention은 RetentionPolicy.CLASS이다.
//2) (CLASS) JVM에 클래스가 로딩될때는 사라진다. - 클래스 시점에만 정보가 있는데 예제는 Runtime 시점에 정보를 알고 있어야 하므로 RUNTIME으로 변경해야한다.
//2) 제일 오래 살아남는게 RetentionPolicy.RUNTIME이므로 대부분 케이스가 RetentionPolicy.RUNTIME 으로 설정한다.
//3) @Import(HelloImportBeanDefinitionRegistrar.class) //
@interface EnableConfig {
// 속성을 줄 수 있는 방법 (ImportAware)
// String name();
// type에 따른 Import 분기처리 (ImportSelector)
// int type;
}
// ImportAware 를 통한 확장 방법
@Configuration
class HelloConfig implements ImportAware {
@Bean
Hello hello() {
return new Hello("Test");
}
@Autowired
Hello hello; // 위의 @Bean 에 생성한 bean을 Autowired 한다.
@Override
public void setImportMetadata(AnnotationMetadata importMetadata) {
// Import 해서 HelloConfig를 Bean으로 등록하는 시점에
// AnnotationMetadata란 해당 HelloConfig 를 최초로 가져오게된 시점의 클래스, 메타데이터 정보 담는다.
// 해당 샘플코드에서는 AppConfig의 @EnableHello("Spring") 선언 정보이다.
// 즉 Enable에 설정된 옵션을 적용해서 확장할 수 있다.
String name = (String) importMetadata.getAnnotationAttributes(EnableHello.class.getName()).get("name");
hello.setName(name);
}
}
// ImportSelector 를 통한 확장 방법
// @Configuration이 안붙어있다. 조건만 정의하면 되기 때문에 Bean이 될 필요가 없다.
class HelloSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
// AnnotationMetadata를 참조해보니 경우에 따라 Configuration을 선별적으로 Import 시키면 된다를 풀어낸다.
// 하나 이상의 Configuration을 리턴한다.
Intger name = (Integer) importingClassMetadata.getAnnotationAttributes(EnableHello.class.getName()).get("type");
if (type == 1) {
return new String[] {HelloConfig1.class.getName()};
} else {
return new String[] {HelloConfig2.class.getName()};
}
}
}
// 옵션에 따라 등록되는 빈의 종류와 수가 복잡한 방식으로 변경된다면 고려해볼만하다.
public class HelloImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// BeanDefinitionRegistry - 빈 정의를 코드로 만들어서 등록할 수 있는 방법을 제공한다.
// BeanDefinition을 통해서 hello bean을 정의한다.
// 해당 기술을 사용해서 좀더 구체적으로 Bean들을 설정할 수 있다.
BeanDefinition beanDefinition = new RootBeanDefinition(Hello.class); // Bean 하나를 정의하는 방법
beanDefinition.getPropertyValues().addPropertyValue("name", "TEST");
registry.registerBeanDefinition("hello", beanDefinition);
}
}
@Configuration
@EnableHello
public class AppConfig implements NameConfigurer {
@Override
public void configure(Hello hello) {
hello.setName("END");
}
}
@Import(HelloConfig.class)
@interface EnableConfig {
}
// Configurer
interface NameConfigurer {
Hello configure(Hello hello);
}
@Configuration
class HelloConfig {
// AppConfig Bean이 주입이 된다. @Configuration도 Bean이다.
@Autowired
NameConfigurer configurer;
@Bean
Hello hello() {
return configurer.configure(new Hello("TEST"));
}
}
해당 자바코드의 소스는 Spring 4.3.13 Version 의 소스입니다. 기본 골격이 되는 인터페이스 및 어노테이션 정보들로 잦은 변화는 적은 소스입니다.
package org.springframework.context.annotation;
/**
* Indicates one or more {@link Configuration @Configuration} classes to import.
*
* <p>Provides functionality equivalent to the {@code <import/>} element in Spring XML.
* Allows for importing {@code @Configuration} classes, {@link ImportSelector} and
* {@link ImportBeanDefinitionRegistrar} implementations, as well as regular component
* classes (as of 4.2; analogous to {@link AnnotationConfigApplicationContext#register}).
*
* <p>{@code @Bean} definitions declared in imported {@code @Configuration} classes should be
* accessed by using {@link org.springframework.beans.factory.annotation.Autowired @Autowired}
* injection. Either the bean itself can be autowired, or the configuration class instance
* declaring the bean can be autowired. The latter approach allows for explicit, IDE-friendly
* navigation between {@code @Configuration} class methods.
*
* <p>May be declared at the class level or as a meta-annotation.
*
* <p>If XML or other non-{@code @Configuration} bean definition resources need to be
* imported, use the {@link ImportResource @ImportResource} annotation instead.
*
* @author Chris Beams
* @author Juergen Hoeller
* @since 3.0
* @see Configuration
* @see ImportSelector
* @see ImportResource
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {
/**
* {@link Configuration}, {@link ImportSelector}, {@link ImportBeanDefinitionRegistrar}
* or regular component classes to import.
*/
Class<?>[] value();
}
-
package org.springframework.context.annotation;
/**
* Interface to be implemented by types that determine which @{@link Configuration}
* class(es) should be imported based on a given selection criteria, usually one or more
* annotation attributes.
*
* <p>An {@link ImportSelector} may implement any of the following
* {@link org.springframework.beans.factory.Aware Aware} interfaces, and their respective
* methods will be called prior to {@link #selectImports}:
* <ul>
* <li>{@link org.springframework.context.EnvironmentAware EnvironmentAware}</li>
* <li>{@link org.springframework.beans.factory.BeanFactoryAware BeanFactoryAware}</li>
* <li>{@link org.springframework.beans.factory.BeanClassLoaderAware BeanClassLoaderAware}</li>
* <li>{@link org.springframework.context.ResourceLoaderAware ResourceLoaderAware}</li>
* </ul>
*
* <p>ImportSelectors are usually processed in the same way as regular {@code @Import}
* annotations, however, it is also possible to defer selection of imports until all
* {@code @Configuration} classes have been processed (see {@link DeferredImportSelector}
* for details).
*
* @author Chris Beams
* @since 3.1
* @see DeferredImportSelector
* @see Import
* @see ImportBeanDefinitionRegistrar
* @see Configuration
*/
public interface ImportSelector {
/**
* Select and return the names of which class(es) should be imported based on
* the {@link AnnotationMetadata} of the importing @{@link Configuration} class.
*/
String[] selectImports(AnnotationMetadata importingClassMetadata);
}
package org.springframework.context.annotation;
/**
* Interface to be implemented by types that register additional bean definitions when
* processing @{@link Configuration} classes. Useful when operating at the bean definition
* level (as opposed to {@code @Bean} method/instance level) is desired or necessary.
*
* <p>Along with {@code @Configuration} and {@link ImportSelector}, classes of this type
* may be provided to the @{@link Import} annotation (or may also be returned from an
* {@code ImportSelector}).
*
* <p>An {@link ImportBeanDefinitionRegistrar} may implement any of the following
* {@link org.springframework.beans.factory.Aware Aware} interfaces, and their respective
* methods will be called prior to {@link #registerBeanDefinitions}:
* <ul>
* <li>{@link org.springframework.context.EnvironmentAware EnvironmentAware}</li>
* <li>{@link org.springframework.beans.factory.BeanFactoryAware BeanFactoryAware}
* <li>{@link org.springframework.beans.factory.BeanClassLoaderAware BeanClassLoaderAware}
* <li>{@link org.springframework.context.ResourceLoaderAware ResourceLoaderAware}
* </ul>
*
* <p>See implementations and associated unit tests for usage examples.
*
* @author Chris Beams
* @since 3.1
* @see Import
* @see ImportSelector
* @see Configuration
*/
public interface ImportBeanDefinitionRegistrar {
/**
* Register bean definitions as necessary based on the given annotation metadata of
* the importing {@code @Configuration} class.
* <p>Note that {@link BeanDefinitionRegistryPostProcessor} types may <em>not</em> be
* registered here, due to lifecycle constraints related to {@code @Configuration}
* class processing.
* @param importingClassMetadata annotation metadata of the importing class
* @param registry current bean definition registry
*/
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Conditional {
/**
* All `Condition`s that must `Condition#matches` match in order for the component to be registered.
*/
Class<? extends Condition>[] value();
}
public interface Condition {
/**
* Determine if the condition matches.
*/
boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}
[2018/01/03 11:42:06.012][http-apr-31883-exec-3][ERROR][AbstractStep:225] Encountered an error executing step SomethingJobStep in job SomethingJob
org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [org.springframework.transaction.PlatformTransactionManager] is defined: expected single matching bean but found 2: transactionManager,resourcelessTransactionManager
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:365) ~[spring-beans-4.1.7.RELEASE.jar:4.1.7.RELEASE]
PlatformTransactionManager를 bean 으로 2개 이상 등록한 경우 실제 어떤 bean을 선택해서 서비스해야할지 판단하기 어려운 케이스입니다.
@Bean
public PlatformTransactionManager transactionManager(@Qualifier("masterDataSource") DataSource masterDataSource) {
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(masterDataSource);
transactionManager.setGlobalRollbackOnParticipationFailure(false);
return transactionManager;
}
@Bean(name = "resourcelessTransactionManager")
public ResourcelessTransactionManager resourcelessTransactionManager() {
ResourcelessTransactionManager resourcelessTransactionManager = new ResourcelessTransactionManager();
return resourcelessTransactionManager;
}
@Transactional("transactionManager")
public void someMethod() {
}
@Primary
@Bean
public PlatformTransactionManager transactionManager(@Qualifier("masterDataSource") DataSource masterDataSource) {
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(masterDataSource);
transactionManager.setGlobalRollbackOnParticipationFailure(false);
return transactionManager;
}
package org.springframework.beans.factory;
/**
* Marker superinterface indicating that a bean is eligible to be
* notified by the Spring container of a particular framework object
* through a callback-style method. Actual method signature is
* determined by individual subinterfaces, but should typically
* consist of just one void-returning method that accepts a single
* argument.
* @author Chris Beams
* @since 3.1
*/
public interface Aware {
}
트랜잭션 간의 격리를 의미 레벨이 높을 수록 동시성은 떨어지고 일관성은 높아진다. 즉 변경에 대해 더 방어적이게 된다.
Shared-Lock 이 걸리지 않는 레벨 A라는 데이터를 B로 변경하는 동안에는 다른 사용자는 해당 데이터에 접근할 수 있다.
Shared-Lock 이 걸리는 레벨 A라는 데이터를 B로 변경하는 동안에는 다른 사용자는 해당 데이터에 접근할 수 없다.
트랜잭션이 완료될 때까지 SELECT 문장이 사용하는 모든 데이터에 Shared Lock이 걸리므로 다른 사용자는 그 영역에 해당되는 데이터에 대한 수정이 불가능합니다. 가령, Select col1 from A where col1 between 1 and 10을 수행하였고 이 범위에 해당하는 데이터가 2건이 있는 경우(col1=1과 5) 다른 사용자가 col1이 1이나 5인 Row에 대한 UPDATE이 불가능합니다. 하지만, col1이 1과 5를 제외한 나머지 이 범위에 해당하는 Row를 INSERT하는 것이 가능합니다.
트랜잭션이 완료될 때까지 SELECT 문장이 사용하는 모든 데이터에 Shared Lock이 걸리므로 다른 사용자는 그 영역에 해당되는 데이터에 대한 수정 및 입력이 불가능합니다. 예를 들어, Repeatable Read의 경우 1에서 10 사이에 해당되는 데이터에 대한 UPADTE이 가능하였습니다. 하지만 이 Level에서는 UPDATE 작업도 허용하지 않습니다.
읽기 연산을 수행하기 전에 획득하는 Lock , 여러 트랜잭션이 획득 가능 isolation level에 따라서 읽은 후에 바로 Lock 을 푸는 경우가 있고 풀지 않는 경우가 있다. case 1) READ COMMITTED(4)인 경우 트랜잭션 T1이 커밋되기 전이라도 읽기 연산을 완료하면 획득한 공유 잠금을 즉시 해제 다른 트랜잭션 중 하나가 해당 객체에 대한 갱신 또는 삭제 연산을 수행할 수 있다. case 2) EPEATABLE READ(5)인 경우, 트랜잭션 T1이 커밋될 때까지 공유 잠금을 유지 다른 트랜잭션 중 하나가 해당 객체에 대한 갱신 또는 삭제 연산을 수행할 수 없다.
객체에 대한 갱신연산을 수행하기 전
에 획득함. 하나의 트랜잭션만 획득
트랜잭션 T1이 특정 객체 X에 대해 갱신 연산을 수행하기 전에 배타 잠금을 먼저 획득하고,
갱신 연산을 완료하더라도 트랜잭션 T1이 커밋될 때까지 배타 잠금을 해제하지 않는다.
따라서, 트랜잭션 T2, T3은 트랜잭션 T1이 배타 잠금을 해제하기 전까지는 X에 대한 읽기 연산도 수행할 수 없다.
갱신연산을 수행하기 전, 조건절에서 읽기 연산을 수행할 때 획득하는 잠금이다. 즉, update … where … , delete … where 문에서 사용된다.
예를 들어 WHERE 절과 결합된 UPDATE 문을 수행하는 경우,
WHERE 절에서 인덱스 검색을 하거나 풀 스캔 검색을 수행할 때 행 단위로 갱신 잠금을 획득하고,
조건을 만족하는 결과 행들에 대해서만 배타 잠금을 획득하여 갱신 연산을 수행한다.
이처럼 갱신 잠금은 실제 갱신 연산을 수행할 때 배타 잠금으로 변환되며,
이는 다른 트랜잭션이 동일한 객체에 대해 읽기 연산을 수행하지 못하도록 하므로 준 배타 잠금이라고 할 수 있다.
키가 존재하는 행에 대해서는 CRUD 작업시에 키에 대한 잠금을 획득한다. INSERT = 해당 키에 X_LOCK, 해당키와 다음키에 NS_LOCK을 획득한다. UPDATE/DELETE = 지정한 범위에 해당하는 모든 키와 범위내 가장 마지막 키의 다음키에 NX_LOCK을 획득한다.
고유 키가 존재하는 행에 대해 Insert 를 수행할 때 해당 작업이 영향을 주는 범위를 보호하기 위해 다음키에 대핸 잠금을 획득한다.
고유 키가 존재하는 행에 대해서 UPDATE, DELETE 작업 수행시 해당 작업이 영향을 주는 범위를 보호하기 위해 이전키와 다음키에 잠금을 획득한다.
JCL = 아파치(자카르타) Commons Logging
JCL은 로깅 추상화 라이브러리이다.
JUL (Java Utility Logging) 도 쓸 수 있고, Log4J도 있고, SLF4J도 있고~~
라이브러리나 프레임워크 개발자는 로깅 추상화 라이브러리 만 쓰고 실제 어플리케이션을 개발하는 개발자가 구체화된 것을 선택할 수 있다.
구현체 찾는 법
최근에는 JCL 을 꺼린다 그 대신 SLF4J 를 사용하려 한다.
SLF4J = Simple Logger Factory For Java JCL과는 클래스로더 방법이 다르다 컴파일 시점에 사용할 로거가 정의된다.
구성요소 : 브릿징, API, 바인딩 API : if문으로 감싸지 않아도 된다. 바인딩 : 여러 로거로 연결해주는 작업을 한다. (Log4J) 프레임워크, 라이브러리에서는 쓰지 않는다. 어플리케이션 개발자가 선택해야 하는 것 딱 하나만 넣어야 한다. (JDK14, JCL, Logback, LOG4J) 브릿징 : 구현체 호출을 SLF4J가 호출한 것으로 바꾼다. 다양한 라이브러리 에서 사용한 다양한 로깅 구현체들이 SLF4J 로 흘러가게 만든다. 레거시 코드에서 중요한 역할을 한다. ex) log4j-over-slfj.jar
사용법 내가 바인더를 logback을 선택하였다면 logback을 제외한 바인딩 들을 모두 클래스 패스에 추가한다. 바인딩이라는 것은 가짜 구현체라고 보면 된다.
스프링 부트는 무엇을 한것일까요?
eval eval is evil 보안, 퍼포먼스 코드의 컨텍스트 변환 등으로 인한 유지보수 상 사용금지
with의 모호성
value가 어떤 value를 말하는 지 알 수 있을까? obj.value? parameter value?
그래서 ECMAScript 6 에서는 with 구문이 제외 되었다.
function doSomething(value, obj){
with (obj) {
console.log(value);
value = "which scope is this?"
}
}
스코프란 현재 접근할 수 있는 변수들의 범위를 의미한다. 현재 위치에서 볼 수 있는 변수들의 범위
var i, len=3;
for (i = 0; i <len; i++) {
document.getElementById("div" + i).addEventListener("click", function () {
alert("You clicked div #" + i);
}, false);
}
위의 코드는 마치 div0, div1, div2 를 클릭하면 각각 0,1,2 가 나올거 같지만 모두 div3이 찍힌다. 그 이유는 해당 function이 바라보는 i는 Global Scope에 있는 i 이기 때문이다. Global Scope의 i는 for() 수행이 끝나고 난 3이 되어 있는 상황이다.
자바스크립트에서 Scope가 생성되는 구문은 다음과 같다 with function catch
특정 함수가 참조하는 변수들이 선언된 렉시컬 스코프는 계속 유지되는데, 그 함수와 스코프를 묶어서 클로져라고 한다.
function outer() {}
var count = 0;
var inner = function () {
return ++count;
}
return inner;
}
var increase = outer();
outer() 의 count 변수는 마치 outer 객체의 private 변수 같은 느낌이다.
function outer() {
var count = 0;
return {
increase : function () {
return count++;
},
decrease : function () {
return --count;
}
}
}
var counter = outer();
counter.increase(); // 1
counter.increase(); // 2
counter.decrease(); // 1
var counter2 = outer();
counter2.increase(); // ?
counter2.increase(); 는 무슨 값이 나올까? 정답은 1이다. 그 이유는 함수를 호출할 때마다 별도의 scope를 가지기 때문이다.